[Alibaba微服务技术入门]_Sentinel熔断降级_第13讲
熔断降级官方地址:https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
熔断降级概述:除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置
熔断策略
Sentinel 提供以下几种熔断策略
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
RT(平均响应时间,秒级)
- 平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级窗口期过后关闭断路器
- RT最大4900(更大的需要通过-Desp.sentinel.statistic.max.rt=XXXX力能生效)
异常比列(秒级)
- QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数(分钟级)
- 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
注意:Sentinel在1.8.0版本对熔断降级做了大的调整,可以定义任意时长的熔断时间,引入了半开启恢复支持
下面梳理一下,Sentinel1.8.0版本的相关特性
(一)熔断状态
熔断有三种状态,分别为OPEN、HALF_OPEN、CLOSED
(二)熔断三种策略相关属性解释
1.慢调用比例
执行逻辑
- 熔断(OPEN):请求数大于最小请求数并且慢调用的比率大于比例阈值则发生熔断,熔断时长为用户自定义设置
- 探测(HALFOPEN):当熔断过了定义的熔断时长,状态由熔断(OPEN)变为探测(HALFOPEN)
- 如果接下来的一个请求小于最大RT,说明慢调用已经恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)
- 如果接下来的一个请求大于最大RT,说明慢调用未恢复,继续熔断,熔断时长保持一致
2.异常比例
通过计算异常比例与设置阈值对比的一种策略
执行逻辑
- 熔断(OPEN):当请求数大于最小请求并且异常比例大于设置的阈值时触发熔断,熔断时长由用户设置
- 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
- 如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)
- 如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致
3.异常数
通过计算发生异常的请求数与设置阈值对比的一种策略
执行逻辑
- 熔断(OPEN):当请求数大于最小请求并且异常数量大于设置的阈值时触发熔断,熔断时长由用户设置。探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
- 如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)
- 如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致
(三)熔断降级DegradeRule中的属性进行说明
(四)熔断降级规则示例
1.慢调用策略
[{ "count":3000, "grade": 0, "limitApp": "default", "minRequestAmount": 100, "resource": "degrade01", "slowRatioThreshold": 0.5, "statIntervalMs": 1000, "timeWindow": 5 }]
2.异常比例
{ "count": 0.3, "grade": 1, "limitApp": "default", "minRequestAmount": 200, "resource": "degrade02", "slowRatioThreshold": 1, "statIntervalMs": 1000, "timeWindow": 5 }
3.异常数
{ "count": 1000, "grade": 2, "limitApp": "default", "minRequestAmount": 300, "resource":"degrade03", "slowRatioThreshold": 1, "statIntervalMs": 1000, "timeWindow": 5 }
熔断降级实战
(一)RT(平均响应时间,秒级)
- 1秒钟,持续进入5个请求
- 平均响应时间大于阈值
下图是配置RT的示例图
在FlowLimitController里面新增接口 "/sentinel/testRT"
package com.liuyangjava.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController public class FlowLimitController { @Value("${server.port}") private String port; private Logger logger = LoggerFactory.getLogger(FlowLimitController.class); @GetMapping("/sentinel/service") public String getInfo() { return "sentinel service is running, server port: " + port; } @GetMapping("/sentinel/playInfo") public String playInfo() { logger.info(Thread.currentThread().getName()+"\t"+".../sentinel/playInfo"); return "sentinel service is playInfo"; } @GetMapping("/sentinel/testRT") public String testRT() { try { // 模拟此接口一秒钟才处理完一个请求,其处理请求时间远大于RT响应时间 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "sentinel service RT is Opening"; } }
JMeter进行并发测试
(二)异常比例
- 当资源的每秒请求量>=5
- 并且每秒异常总数占通过量的比值超过阈值(DegradeRule中的count)之后,资源进入降级状态
下图是配置异常比例的示例图
在FlowLimitController中,新增 "/sentinel/testException",此接口就模拟发生异常的请求
package com.liuyangjava.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController public class FlowLimitController { @Value("${server.port}") private String port; private Logger logger = LoggerFactory.getLogger(FlowLimitController.class); @GetMapping("/sentinel/service") public String getInfo() { return "sentinel service is running, server port: " + port; } @GetMapping("/sentinel/playInfo") public String playInfo() { logger.info(Thread.currentThread().getName()+"\t"+".../sentinel/playInfo"); return "sentinel service is playInfo"; } @GetMapping("/sentinel/testRT") public String testRT() { try { // 模拟此接口一秒钟才处理完一个请求,其处理请求时间远大于RT响应时间 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(Thread.currentThread().getName()+"\t"+".../sentinel/testRT"); return "sentinel service RT is Opening"; } @GetMapping("/sentinel/testException") public String testException() { // 触发异常, 如果异常发生比例超过阈值, 服务熔断降级 int i = 10 / 0; return "sentinel service testException is Opening"; } }
JMeter进行并发测试
(三)异常数
- 当单位统计时长内的异常数目超过阈值之后会自动进行熔断
下图是配置异常数的示例图
FlowController就不用新增请求接口,直接通过JMeter进行并发测试即可