微服务—熔断器Hystrix
前言
在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元应用间通过服务注册与发现的方式互相依赖。
由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,
而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会出现因等待出现故障的依赖方响应而形成任务积压,线程资源无法释放,最终导致自身服务的瘫痪,
进一步甚至出现故障的蔓延最终导致整个系统的瘫痪。如果这样的架构存在如此严重的隐患,那么相较传统架构就更加的不稳定。
为了解决这样的问题,因此产生了断路器等一系列的服务保护机制。
为了使得故障隔离,Hystrix提供的解决方案我们需要关注一下几个部分:
- 舱壁隔离(线程隔离)
- 超时控制,服务降级
- 熔断机制
1.仓壁隔离
“舱壁模式”对于熟悉Docker的读者一定不陌生,Docker通过“舱壁模式”实现进程的隔离,使得容器与容器之间不会互相影响。
而Hystrix则使用该模式实现线程池的隔离,它会为每一个Hystrix命令创建一个独立的线程池,这样就算某个在Hystrix命令包装下的依赖服务出现延迟过高的情况,也只是对该依赖服务的调用产生影响,而不会拖慢其他的服务。
如何使用?
我们使用@HystrixCommand来将某个函数包装成了Hystrix命令,Hystrix框架就会自动的为这个函数实现调用的隔离。
Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)
2.超时控制和服务降级
如果服务提供方延迟了自己设置的超时限制,而服务消费方触发了服务请求超时异常,服务消费者就通过HystrixCommand注解中指定的降级逻辑进行执行,
因此该请求的结果返回了fallback
。这样的机制,对自身服务起到了基础的保护,同时还为异常情况提供了自动的服务降级切换机制。
Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)
@Repository @DefaultProperties(groupKey="userDao", //命令执行超时时间,默认1000ms commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")}, threadPoolProperties={@HystrixProperty(name="coreSize",value="10") ,@HystrixProperty(name="maxQueueSize",value="1000")}, threadPoolKey="userDao" ) public class UserDao{ public User getUserByTokenFb(String token){ return new User(); } /** * 调用鉴权服务 * @param token * @return */ @HystrixCommand(fallbackMethod="getUserByTokenFb") public User getUserByToken(String token) { String url = "http://" + userServiceName + "/user/get?token=" + token; ResponseEntity<RestResponse<User>> responseEntity = rest.get(url, new ParameterizedTypeReference<RestResponse<User>>() {}); RestResponse<User> response = responseEntity.getBody(); if (response == null || response.getCode() != 0) { return null; } return response.getResult(); } }
3.熔断机制
“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),直接切断原来的主逻辑调用。
那么当断路器打开之后会发生什么呢?我们先来说说断路器未打开之前,对于之前那个示例的情况就是每个请求都会在当hystrix超时之后返回fallback
,每个请求时间延迟就是近似hystrix的超时时间,
如果设置为5秒,那么每个请求就都要延迟5秒才会返回。当熔断器在10秒内发现请求总数超过20,并且错误百分比超过50%,这个时候熔断器打开。
打开之后,再有请求调用的时候,将不会调用主逻辑,而是直接调用降级逻辑,这个时候就不会等待5秒之后才返回fallback。
通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能.
熔断器模式有三种状态:
关闭、打开、半开
熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.服务的健康状况 = 请求失败数 / 请求总数.
-
当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
-
当熔断器开关打开时, 请求被禁止通过.
-
当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.
Hystrix使用命令模式
Hystrix使用命令模式(继承HystrixCommand类)来包裹具体的服务调用逻辑(run方法), 并在命令模式中添加了服务调用失败后的降级逻辑(getFallback).
同时我们设置当前服务线程池和熔断器的相关参数.
附上一张终极原理图:
-
构建Hystrix的Command对象, 调用执行方法.
-
Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法.
-
若熔断器开关关闭, 则Hystrix检查当前服务的线程池是否能接收新的请求, 若超过线程池已满, 则执行降级服务getFallback方法.
-
若线程池接受请求, 则Hystrix开始执行服务调用具体逻辑run方法.
-
若服务执行失败, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
-
若服务执行超时, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
-
若服务执行成功, 返回正常结果.
-
若服务降级方法getFallback执行成功, 则返回降级结果.
-
若服务降级方法getFallback执行失败, 则抛出异常.
Spring Cloud构建微服务架构:服务容错保护(Hystrix断路器)
注:一些配置
要使用hystrix需要在pom文件中添加下面两个依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.4.RELEASE</version> </dependency>
服务消费者application.properties
#命令执行超时时间,默认1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000 hystrix.threadpool.default.coreSize=5 hystrix.threadpool.default.maxQueueSize=1 #错误比率阀值,如果错误率>=该值,circuit会被打开,并短路所有请求触发fallback。默认50 hystrix.command.default.circuitBreaker.errorThresholdPercentage=10 #休眠时间窗 hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=100000 #必须要配置这个暴露监控端口,这样才能hystrix dashboard查看监控信息 management.endpoints.web.exposure.include=*
这是做的全局默认配置,如果在某个服务级别上单独配置,使用@DefaultProperties在类级别上做配置:
@DefaultProperties(groupKey="userDao", //命令执行超时时间,默认1000ms commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")}, threadPoolProperties={@HystrixProperty(name="coreSize",value="10") ,@HystrixProperty(name="maxQueueSize",value="1000")}, threadPoolKey="userDao" )
另外后还需要加注解@HystrixCommand做方法级别的命令控制,默认使用类级别的参数。
参考:
防雪崩利器:熔断器 Hystrix 的原理与使用
对于监控的状态信息可以通过Hystrix提供的dashboard来直观显示,可安装这篇文章进行操作