微服务组件: Hystrix
一、Hystrix 简介
1、为什么需要容错保护
在微服务架构中,我们将系统的按功能拆分了很多服务单元,各个服务单元都被注册在注册中心中。通过从注册中心获取的其他服务地址,来调用其他服务。实现服务之间的调用。
比如:在一个电商网站中,我们可能将系统拆分为用户、订单、积分等服务单元。当用户创建一个订单的时候,

各个服务单元相互调用,相互依赖。由于每个服务单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为各种原因导致出现延迟,出现调用故障等。而这些问题直接导致调用方的对外服务出现延迟或故障。
比如:在电商网站中,用户服务和订单服务相互调用,当用户创建一个订单时,客户端将调用订单服务的创建订单接口,如果此时创建订单时由于自身的逻辑等原因造成响应缓慢,会导致创建订单服务的线程被挂起,只能等待创建订单的响应,甚至可能导致调用失败。在高并发的情况下,这些挂起的线程没有响应,并且后续有大量的请求被阻塞,最终导致订单服务也不可能用。
在微服务架构中,有这么多的服务单元,且服务之间需要相互调用,如果一个单元出现故障,就可能因依赖关系引发故障的蔓延,最终道中整个系统的瘫痪。
因此,我们需要引入服务容错保护组件 — Hystrix
。
2、什么是 Hystrix
Spring Cloud Hystrix
实现了断路器、线程隔离等一系列服务保护功能。
1 2 3 4 5 6
| # 什么是 断路器
断路器是一种 “开关” 装置。 用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时切断故障电路。 防止发生过载、发热甚至起火等严重后果。
|
在分布式架构中,断路器模式的作用也类似的,当某个服务单元发生故障(类似于电器发生短路)之后,通过断路器的故障监控(类似于熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程一直被挂起。避免的之后的调用被阻塞。
Hystrix
组件的目的在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
Hystrix
组件具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| # 服务熔断
服务熔断的作用类似于保险丝,当某个服务出现不可用时或响应超时时 为了防止 “服务雪崩” 暂停对该服务的调用
# 服务降级: 为了预防某些服务出现负荷过载或者响应慢的情况,我们舍弃一些边缘服务(非核心业务或功能)。 直接返回一个提前准备号的fallback错误信息。
# 相同点 防止系统的整体缓慢甚至崩溃,采用的技术手段; 最终让用户体验到的是某些功能暂时不可达或不可用;
# 不同点 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑
# 注意 熔断必会触发降级,所以熔断也是降级一种。 区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理
|
二、Hystrix 实例 – 服务熔断
注册中心为Consul
,使用声明式调用组件OpenFeign
。
两个服务,具体的服务实例查看另一篇博客。服务调用组件。
添加Hystrix
依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
|
添加注解
1 2 3 4 5 6 7 8 9 10 11
| @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker public class Products8882Application {
public static void main(String[] args) { SpringApplication.run(Products8882Application.class, args); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @RestController @Slf4j public class ProductsController {
@GetMapping("/product/break") @HystrixCommand(fallbackMethod = "testBreakFall") public String testBreak(Integer id){ log.info("接受商品的id" + id);
if(id < 0){ throw new RuntimeException("数据不合法"); } return "当前接受商品id:" + id; }
public String testBreakFall(Integer id){ return "对不起,当前数据不合法: " + id + "请重新输入"; } }
|
用户服务通过OpenFeign
来调用商品服务。
接口
1 2 3 4 5 6
| @FeignClient("PRODUCT") public interface ProductClient {
@GetMapping("/product/break") String testBreak(@RequestParam("id") Integer id); }
|
1 2 3 4 5 6 7 8
| @RestController @Slf4j public class UserController { @GetMapping("/user/break") public String breakTest(Integer id){ return productClient.testBreak(id); } }
|
当我们输入正确的数值:

当我们输入错误的数值:

我们可以看到,当我们使用异常来模拟,出现异常时,断路器打开,此时出现服务熔断。那么就会调用设置的响应内容。
三、Hystrix实例 – 服务降级
服务熔断的实现是在被调用方,是服务端。而服务降级是在调用方,属于客户端。
可以通过OpenFeign
和Hystrix
实现服务降级。
引入依赖:
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
|
加入注解
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableFeignClients @EnableHystrix public class User8881Application { public static void main(String[] args) { SpringApplication.run(User8881Application.class, args); } }
|
开启OpenFeign
支持服务降级
1
| feign.hystrix.enabled=true
|
开发fallback
处理类,并继承接口,不要忘记加入@Component
注解
1 2 3 4 5 6 7
| @Component public class ProductFallBack implements ProductClient { @Override public String testBreak(Integer id) { return "fallback 处理方法"; } }
|
在接口上加入被调用方的方法
1 2 3 4 5
| @FeignClient(value = "PRODUCT",fallback = .class) public interface ProductClient { @GetMapping("/product/break") String testBreak(@RequestParam("id") Integer id); }
|
我们正常访问localhost:8881/user/break?id=1

我们商品服务关闭,再次访问localhost:8881/user/break?id=1

此时,服务端商品服务已经宕机了,但是我们进行了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。
四、使用细节
断路器打开的条件:
https://cloud.spring.io/spring-cloud-netflix/2.2.x/reference/html/#circuit-breaker-spring-cloud-circuit-breaker-with-hystrix
1 2 3 4 5 6 7
| # 原文翻译之后,总结打开关闭的条件: - 1、 当满足一定的阀值的时候(默认10秒内超过20个请求次数) - 2、 当失败率达到一定的时候(默认10秒内超过50%的请求失败) - 3、 到达以上阀值,断路器将会开启 - 4、 当开启的时候,所有请求都不会进行转发 - 5、 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。 如果成功,断路器会关闭,若失败,继续开启。重复4和5。
|
我们做一个服务熔断中断路器的打开与关闭的实例:
我们访问http://localhost:8881/user/break?id=-2
。

然后,疯狂刷新,10秒内刷新20个请求。
访问地址http://localhost:8881/user/break?id=9

可以看到,数据合法,但断路器依旧打开了。5 s之后再次访问,恢复正常。
