Hystrix是一个容错和限流工具组件。Hystrix主要具备服务降级、服务熔断和服务监控等的功能。
其中容错是依靠降级,限流是通过熔断。
当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个错误相应,而不是长时间等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中蔓延。
如果请求后台服务出错,就会执行另一段代码,向客户端返回结果。降级的响应结果可以是错误信息,也可以是缓存数据。
当服务压力过大,或者错误比例过多时,熔断所有请求,所有请求直接降级。一台服务器故障,可能造成雪崩效应。熔断可以快速断开故障服务,保护其他服务不受影响。
创建一个maven项目模块
整个项目结构大致为两个商品模块(集群),一个用户模块,一个订单模块,并且分别将其注册到两个Eureka服务器中。
由于ribbon和hystrix一般会同时出现,所以这里全都单独使用
首先配置ribbon环境,可以参考Ribbon(一)和Ribbon(二)两篇文章
Ribbon(一)远程调用RestTemplate_바 보71的博客-CSDN博客
Ribbon(二)负载均衡和重试_바 보71的博客-CSDN博客
以下单独提出配置Ribbon的部分
分别添加Eureka依赖、Ribbon依赖和retry依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.retry
spring-retry
由于Eureka中已经包含了Ribbon的负载均衡,所以Ribbon的依赖可以不添加
服务名称先为ribbon,后续修改为hystrix
并且配置retry
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
#单台服务器的重试次数
MaxAutoRetries: 1
#更换服务器次数
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: true
#OkToRetryOnAllOperations 是否对所有类型请求都重试,默认只对GET重试
添加@EnableDiscoveryClient注解
添加RestTemplate实例,并且设置超时时间等属性
在RestTemplate实例上添加注解@LoadBalanced
package cn.tedu.sp06;
...
@EnableDiscoveryClient //都是能够让注册中心能够发现,扫描到改服务
@SpringBootApplication
public class Sp06RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
//创建 RestTemplate 实例,并存入 spring 容器
@LoadBalanced
//@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
//设置超时时间1s
//连接已建立并发送请求,等待响应结果的超时时间
f.setReadTimeout(1000);
//建立连接等待超时时间为1s
f.setConnectTimeout(1000);
return new RestTemplate(f);
//RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1,
//未启用超时,也不会触发重试
//return new RestTemplate();
}
}
package cn.tedu.sp06.controller;
...
@RestController
public class RibbonController {
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult> getItems(@PathVariable String orderId) {
//这里服务器路径用 service-id 代替,ribbon 会向服务的多台集群服务器分发请求
return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List- items) {
return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
//
@GetMapping("/user-service/{userId}")
public JsonResult
getUser(@PathVariable Integer userId) {
return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult addScore(
@PathVariable Integer userId, Integer score) {
return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
}
//
@GetMapping("/order-service/{orderId}")
public JsonResult getOrder(@PathVariable String orderId) {
return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
}
@GetMapping("/order-service")
public JsonResult addOrder() {
return rt.getForObject("http://order-service/", JsonResult.class);
}
}
将上一步创建的项目名称改为hystrix (可选)
添加hystrix的依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
修改服务名称为hystrix
添加hystrix超时设置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
spring:
application:
name: hystrix
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
ribbon:
#单台服务器的重试次数
MaxAutoRetries: 1
#更换服务器次数
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: true
#OkToRetryOnAllOperations 是否对所有类型请求都重试,默认只对GET重试
#hystrix等待超时后, 会执行降级代码, 快速向客户端返回降级结果, 默认超时时间是1000毫秒
#为了测试 hystrix 降级,我们把 hystrix 等待超时设置得非常小(500毫秒)
#此设置一般应大于 ribbon 的重试超时时长,例如 10 秒
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
添加@EnableCircuitBreaker注解
通过@EnableCircuitBreaker注解可以启用hystrix容器
当主程序中同时存在@EnableCircuitBreaker、@EnableDiscoveryClient、和@SpringBootApplication三个注解时,可以通过@SpringCloudApplication注解代替
package cn.tedu.sp06;
import ...;
//@EnableCircuitBreaker //启用 hystrix 断路器
//@EnableDiscoveryClient //都是能够让注册中心能够发现,扫描到改服务
//@SpringBootApplication
@SpringCloudApplication //可以使用 @SpringCloudApplication 注解代替三个注解
public class Sp06RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
//设置超时时间1s
f.setReadTimeout(1000);
f.setConnectTimeout(1000);
return new RestTemplate(f);
}
}
为其中的方法添加@HystrixCommand注解,指定降级方法,格式为:
@HystrixCommand(fallBackMethod="降级方法名称")
添加具体的降级方法,例如getItems()的降级方法为getItemsFB()
方法名称可以随意,这里为了区分两种方法,并且返回结果为错误信息
package cn.tedu.sp06.controller;
import ...;
@RestController
@Slf4j
public class RibbonController {
@Autowired
private RestTemplate rt;
//为每个方法添加降级方法:方法名FB()
//添加@HystrixCommand 注解,指定降级方法名
@GetMapping("/item-service/{orderId}")
@HystrixCommand(fallbackMethod = "getItemsFB") //指定降级方法的方法名
public JsonResult> getItems(@PathVariable String orderId) {
// 调用远程 02项目,获取商品列表
//{1},{2}是RestTamplate提供的一种占位符
//RestTemplate必须给出具体的服务器地址
//Ribbon对RestTemplate进行了增强,提供负载均衡的功能
//根据注册表的主机地址列表,做负载均衡访问
return rt.getForObject(
"http://item-service/{1}",//给出具体服务器的地址
JsonResult.class,
orderId);
}
@PostMapping("/item-service/decreaseNumber")
@HystrixCommand(fallbackMethod ="decreaseNumberFB")
public JsonResult> decreaseNumber(@RequestBody List- items) {
return rt.postForObject(
"http://item-service/decreaseNumber",
items,
JsonResult.class);
}
//
@GetMapping("/user-service/{userId}")
@HystrixCommand(fallbackMethod ="getUserFB")
public JsonResult
getUser(@PathVariable Integer userId) {
return rt.getForObject(
"http://user-service/{1}",
JsonResult.class,
userId);
}
@GetMapping("/user-service/{userId}/score")
@HystrixCommand(fallbackMethod ="addScoreFB")
public JsonResult addScore(@PathVariable Integer userId, Integer score) {
return rt.getForObject(
"http://user-service/{1}/score?score={2}",
JsonResult.class,
userId, score);
}
//
@GetMapping("/order-service/{orderId}")
@HystrixCommand(fallbackMethod ="getOrderFB")
public JsonResult getOrder(@PathVariable String orderId) {
return rt.getForObject(
"http://order-service/{1}",
JsonResult.class,
orderId);
}
@GetMapping("/order-service")
@HystrixCommand(fallbackMethod ="addOrder")
public JsonResult> addOrder() {
return rt.getForObject("http://order-service/",
JsonResult.class);
}
//
//降级方法的参数和返回值,需要和原始方法一致,方法名任意
public JsonResult> getItemsFB(String orderId) {
return JsonResult.err("获取订单商品列表失败");
}
public JsonResult decreaseNumberFB(List- items) {
return JsonResult.err("更新商品库存失败");
}
public JsonResult
getUserFB(Integer userId) {
return JsonResult.err("获取用户信息失败");
}
public JsonResult addScoreFB(Integer userId, Integer score) {
return JsonResult.err("增加用户积分失败");
}
public JsonResult getOrderFB(String orderId) {
return JsonResult.err("获取订单失败");
}
public JsonResult addOrderFB() {
return JsonResult.err("添加订单失败");
}
}
将user-serivce和order-service服务断开,访问会执行对应的降级方法
在item-service中编写了可能会超时的代码,所以两种结果,成功访问会获取商品信息,失败则执行降级方法