熔断与降级
Sentinel:
支持的规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 、热点参数规则
一、流量控制:
设计理念:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
二、熔断降级:
设计理念:
-
通过并发线程数进行限制:
当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
-
通过响应时间对资源进行降级:
当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
三、网关流控:Gateway + Sentinel + Nacos动态规则:
-
添加依赖:
<!-- SpringCloud Ailibaba Sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- SpringCloud Ailibaba Sentinel Gateway --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency> <!-- Sentinel Datasource Nacos --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
-
在配置文件添加配置:
spring: cloud: nacos: discovery: # 服务注册地址 server-addr: 127.0.0.1:8848 config: # 配置中心地址 server-addr: 127.0.0.1:8848 # 配置文件格式 file-extension: yml # 共享配置 shared-configs: - ${spring.cloud.nacos.config.file-extension} sentinel: eager: true #控制台热加载,false为懒加载(第一次调用接口才会加载) transport: dashboard: 127.0.0.1:8858 datasource: ds1: nacos: server-addr: ${spring.cloud.nacos.config.server-addr} dataId: ${spring.application.name} groupId: SENTINEL_GROUP data-type: json rule-type: gw-flow namespace: ${sentinel.nacos.namespace}
关于rule-type的值:
/** * flow 流控规则 */ FLOW("flow", FlowRule.class), /** * degrade 降级规则 */ DEGRADE("degrade", DegradeRule.class), /** * param flow 热点规则 */ PARAM_FLOW("param-flow", ParamFlowRule.class), /** * system 系统规则 */ SYSTEM("system", SystemRule.class), /** * authority 授权规则 */ AUTHORITY("authority", AuthorityRule.class), /** * gateway flow 网关限流规则 */ GW_FLOW("gw-flow","com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"), /** * api 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合 */ GW_API_GROUP("gw-api-group","com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
-
Nacos上新增JSON文件添加网关流控规则:
网关流控规则说明:
Field 说明 默认值 resource 资源名称,网关route或自定义API分组名称(注:网关route这里的值不是route.id,可调试 resourceMode 限流资源类型,网关route【0】或自定义API分组【1】(详细查看 GatewayFlowRule
和SentinelGatewayConstants
)网关route grade 限流阈值类型,QPS【1】或线程数【0】 QPS count 限流阈值,QPS阈值或线程数值 intervalSec 统计时间间隔,单位秒 1秒 controlBehavior 流控效果,目前支持快速失败【0】和匀速排队【1】 快速失败 burst 应对突发请求时额外允许的请求数目 maxQueueingTimeoutMs 匀速排队模式下的最长排队时间,单位毫秒,仅在匀速排队模式下生效 paramItem 参数属性配置,parseStrategy:提取参数策略(0:Clien IP,1:Remote HOST,2:Header,3:请求参数,4:Cookie);fieldName:若提取策略是Header模式或者URL参数模式,则需要指定header名称或URL参数名称;pattern:参数值的匹配模式;matchStrategy:参数值的匹配策略,支持精确匹配,子串匹配和正则匹配。 json文件:
[ { "resource": "xxx-auth", "count": 1, "grade": 1, "limitApp": "default", "strategy": 0, "controlBehavior": 0 } ]
-
docker部署Sentinel Dashboard:
# 拉取镜像
docker pull bladex/sentinel-dashboard
# 启动容器
docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard
用户名/密码:sentinel/sentinel,可访问http://localhost:8858登录,在流控规则可以看到我们的Nacos添加的流控规则
注意事项:1. 启动网关服务时,记得添加网关流控客户端标识(JVM启动参数-Dcsp.sentinel.app.type=1)
2. 遇到在Sentinel Dashboard读取不到Nacos配置的规则,原因是jdk版本的原因,原先是jdk1.8.0_162换成jdk1.8.0_291解决
-
自定义网关限流处理:
法一:
/** * 网关限流配置 */ @Configuration public class GatewayConfig { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelFallbackHandler sentinelGatewayExceptionHandler() { return new SentinelFallbackHandler(); } @Bean @Order(-1) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } } /** * 自定义限流异常处理 */ public class SentinelFallbackHandler implements WebExceptionHandler { private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) { ServerHttpResponse serverHttpResponse = exchange.getResponse(); serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8); DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas); return serverHttpResponse.writeWith(Mono.just(buffer)); } @Override public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { if (exchange.getResponse().isCommitted()) { return Mono.error(ex); } if (!BlockException.isBlockException(ex)) { return Mono.error(ex); } return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); } private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) { return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); } }
法二:
@Configuration public class GatewayConfig { @PostConstruct private void initBlockHandler() { BlockRequestHandler blockRequestHandler = (exchange, t) -> ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(ResultCode.FLOW_LIMITING.toString())); GatewayCallbackManager.setBlockHandler(blockRequestHandler); } }
四、普通流控:
规则说明:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 【1】或线程数模式【0】 | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 判断的根据是资源自身,还是根据其它关联资源 (refResource ),还是根据链路入口 | 根据资源本身 |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式) | 直接拒绝 |
大致同网关流控一致,在服务配置文件的rule-type改成flow即可
自定义异常实现 BlockExceptionHandler接口即可:
@Component
public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
response.setStatus(HttpStatus.ok().status());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
if(e instanceof FlowException){
// objectMapper.writeValue 用于将java对象转位JSON格式返回调用方
new ObjectMapper().writeValue(response.getWriter(), Result.failed(ResultCode.FLOW_LIMITING));
}
}
}
五、整合OpenFeign熔断降级:
- Sentinel提供了三种熔断策略:
- 慢调用比例: 请求响应时间大于设置的RT(即最大的响应时间)则统计为慢调用。触发此熔断策略的条件需要满足两个条件,一是单位统计时长(statIntervalMs)内请求数大于设置的最小请求数,二是慢调用的比例大于阈值,接下来在熔断时长的范围内请求会自动的被熔断。过了熔断时长后,熔断器进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求响应时间小于设置的慢调用RT则结束熔断,若大于设置的慢调用RT则会再次被熔断。
- 异常比例:当单位统计时长请求数大于设置的最小请求数,并且异常的比例大于阈值,则接下来的熔断时长内请求会被自动熔断。
- 异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断。
- 熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
-
整合步骤:
(1)添加依赖:
<!--引入sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <!--去除jackson-dataformat-xml,否则会返回xml文件,而不是JSON--> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </exclusion> </exclusions> </dependency> <!-- Sentinel规则持久化至Nacos配置 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
(2)开启Feign对Sentinel支持:
spring: application: name: xxx-xxx cloud: sentinel: enabled: true eager: true # 取消控制台懒加载,项目启动即连接Sentinel transport: dashboard: localhost:8080 datasource: # 降级规则 degrade: nacos: server-addr: ${spring.cloud.nacos.discovery.server-addr} dataId: ${spring.application.name}-degrade-rules groupId: SENTINEL_GROUP data-type: json rule-type: degrade feign: sentinel: enabled: true
(3) 添加降级规则:
[ { "resource": "GET:http://服务名/api.admin/v1/users/username/{username}", "grade": 2, "count": 1, "timeWindow": 5 } ]
(4)创建容错类:
@Service // 准备 兜底数据,提高用户体验 public class VideoServiceFallBack implements VideoService { @Override public Video findById(int videoId) { Video video = new Video(); video.setTitle("这个是Fallback里面的视频"); return video; } @Override public int save(Video video) { return 0; } }
(5)配置feign容错类:
@FeignClient(value = "服务名", fallback = VideoServiceFallBack.class)
Hystrix:
一、熔断降级:
设计理念:
- 线程池隔离:针对不同的资源分别创建不同的线程池,不同服务调用都发生在不同的线程池中,在线程池排队、超时等阻塞情况时可以快速失败,并可以提供 fallback 机制。
- 信号量隔离:限制对某个资源调用的并发数。
总结:
在熔断降级方面,Sentinel 和 Hystrix 的本质上都是基于熔断器模式(Circuit Breaker Pattern)。Sentinel 与 Hystrix 都支持基于失败比率(异常比率)的熔断降级,在调用达到一定量级并且失败比率达到设定的阈值时自动进行熔断,此时所有对该资源的调用都会被 block,直到过了指定的时间窗口后才启发性地恢复。
在隔离设计上,Sentinel采取的是信号量隔离,而Hystrix提供了线程池隔离和信号量隔离。
Sentinel相比Hystrix提供更多的功能,用户可以根据系统需求,灵活制定策略,从多个维度上保护服务的稳定性。