Sentinel
Sentinel
1. Sentinel 简介
先小结前面学到的组件:
- 注册中心(Nacos):实现服务的注册与发现;
- Ribbon:客户端的负载均衡器,解决服务集群的负载均衡;
- OpenFeign:声明式的 HTTP 客户端,服务间远程调用,并通过 Ribbon 实现负载均衡;
- 配置中心(Nacos):配置信息中心化管理,共享配置,支持动态刷新;
- Sentinel:微服务流量防卫兵。
1.1 服务雪崩效应
服务雪崩(级联失效、级联故障、cascading failure):一个服务不可用,导致依赖它的服务被拖垮,故障沿调用链向上蔓延,最终整个系统崩溃。

解决办法:
- 限流;
- 仓壁模式(隔离):把资源隔离开,一个资源出问题不波及其他资源:

- 断路器(熔断降级)。
1.2 Sentinel 是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
1.2.1 Sentinel 具有以下特征
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况;
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel;
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
1.2.2 Sentinel 分为两个部分
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持;
- 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
1.3 Sentinel 与 Hystrix 对比
Sentinel 是阿里对 Hystrix 的替代。
| 对比项 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量隔离(并发线程数) | 线程池隔离/信号量隔离 |
| 熔断降级策略 | 慢调用比例、异常比率、异常数 | 异常比率 |
| 实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
| 规则配置(rule) | 支持多种数据源 | 支持多种数据源 |
| 扩展性 | 多个扩展点 | 插件的形式 |
| 基于注解的支持 | 支持 | 支持 |
| 调用链路信息 | 支持同步调用 | 不支持 |
| 限流 QPS(query per second) | 基于 QPS / 并发数,支持基于调用关系的限流 | 不支持 |
| 流量整形 | 支持慢启动、匀速器模式 | 不支持 |
| 系统负载保护 | 支持 | 不支持 |
| 实时监控 API | 各式各样 | 较为简单 |
| 控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
| 常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
Hystrix 已经停更说明:

1.4 Sentinel 架构

客户端会内嵌一个和 Dashboard 交互的端口(默认 8719),提供以下接口:
# 接口概览
http://127.0.0.1:8720/api
# 获取资源的metrics信息
http://127.0.0.1:8720/cnode?id=/jifen/{jifenId}
# 获取流控规则接口
http://127.0.0.1:8720/getRules?type=flow
# 设置规则接口
http://127.0.0.1:8720/setRules1.5 Sentinel 控制台安装
注
启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。
控制台功能:
- 实时观看每个资源的访问情况(QPS);
- 通过控制台设置流控规则(QPS、线程数),还可以设置熔断降级的规则(慢调用比例、异常比例、异常数)。
java -jar sentinel-dashboard-1.8.0.jar --server.port=8888
pause1.6 微服务整合 Sentinel
第一步:添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
第二步:配置:
# 默认是8719,如果8719被占用+1,一直到没有被占用
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8888# 开启饥饿加载,当项目启动立即完成初始化,默认是懒加载
spring.cloud.sentinel.eager=true
2. Sentinel 服务流控
Sentinel 通过流量控制(flow control)以及熔断降级来保护系统资源。
2.1 QPS 超过阈值直接失败
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
QPS:Queries-per-second,每秒资源被访问的次数。
当被访问资源的QPS超过阈值,直接进行限流(限制访问),从而保护资源。触发条件:/order/test5 当 QPS > 1 时,会触发流控:

2.2 线程数超过阈值直接失败
触发条件:当 /order/test5 正在工作的线程数 > 1:

JMeter 使用步骤:
- 新建测试计划:

- 添加线程组:


- 添加 HTTP 请求:


- 添加结果观察:

- 发送请求:

2.3 QPS 超过阈值关联失败
触发条件:当关联资源(/order/test6)访问 QPS > 1,那么 /order/test5 就会被流控:

2.4 线程超过阈值关联失败
触发条件:当关联资源(/order/test6)正在工作的线程数(并发数)> 1,那么 /order/test5 就会被流控:

2.5 QPS 超过阈值链路失败

链路流控需要先关闭入口资源的聚合:
feign.sentinel.enabled=true
spring.cloud.sentinel.web-context-unify=false触发条件:saveOrder[jifen save] 有两个入口,分别为 /order/test5 和 /order/test6。当 /order/test5 访问 QPS > 1,那么 /order/test5 就不能访问受保护资源,其他入口照常访问:

2.6 流控效果 Warm-Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。

场景(秒杀开始前的预热):

2.7 流控效果匀速排队
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景:在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注
匀速排队模式暂时不支持 QPS > 1000 的场景。
3. Sentinel 服务熔断降级
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
3.1 熔断降级:慢调用比例
慢调用比例(SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
重要
断路器的工作流程:
- 一旦熔断,断路器的状态是 Open(所有的请求都不能进来);
- 当熔断时长结束,断路器的状态是 Half-Open(可以允许一个请求进来);
- 如果接下来的请求正常,断路器的状态是 Close(资源就自恢复);
- 如果接下来的请求不正常,断路器的状态是 Open。
触发条件:
- 1s 内最小请求数必须大于 5;
- 慢请求 / 总请求 > 0.6。

演示代码(不传 flag 时模拟慢调用):
@RequestMapping("test5")
public String test5(String flag) throws Exception {
if (flag == null) {
// 模拟慢调用:响应时间超过设定的最大RT
Thread.sleep(1800);
}
return "test5";
}3.2 熔断降级:异常比例
异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
断路器的工作流程与慢调用比例相同(Open -> Half-Open -> Close/Open)。
触发条件:
- 1s 内最小请求数大于 5;
- 1s 内 异常数 / 总请求数 > 0.6。

演示代码(不传 flag 时抛异常):
@RequestMapping("test6")
public String test6(String flag) throws Exception {
if (flag == null) {
throw new IllegalArgumentException("参数异常");
}
return "test6";
}3.3 熔断降级:异常数
异常数(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
触发条件:
- 每秒请求数必须大于 5;
- 每秒的异常数必须大于 10【我们设置的阈值】。

4. Sentinel 热点 Key 规则
热点参数流控:针对接口的某个参数值设置不同的 QPS 阈值。
触发条件:
- 必须有一个参数,参数的值可以任意,例如
http://localhost:9002/order/test7?name=jack; - 接口访问 QPS > 1。

参数例外项:
- 第一个参数的值如果为 jack,那么 QPS 阈值为 1;
- 第一个参数的值如果为 rose,那么 QPS 阈值为 5;
- 第一个参数的值如果为其他,那么 QPS 阈值为 1。

注意事项(参数类型与索引必须与实际接口一致):

5. Sentinel 权限规则
权限(授权)规则:根据请求来源判断是否放行。例如 /goods/findById/{id} 对于 cloud-order 不能调用:

Sentinel 判断来源需要实现 RequestOriginParser,这里从请求头获取 source:
package com.wfx.web;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class CustomerRequestOriginParser implements RequestOriginParser {
public String parseOrigin(HttpServletRequest request) {
System.out.println("come in");
// 获取请求头的source
String source1 = request.getHeader("source");
System.out.println(source1);
return source1;
}
}OpenFeign 指定请求头,发送请求:
// 接口声明(根据id获取积分)
@GetMapping(value = "/jifen/{jifenId}")
public Jifen findJifenById(@PathVariable("jifenId") Integer jifenId, @RequestHeader("source") String source);OpenFeign 拦截器统一设置请求头:
package com.wfx.feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
/**
* <p>title: com.wfx.feign</p>
* author zhuximing
* description: 统一为feign请求设置source请求头
*/
@Component
public class CustomerRequestInterceptor implements RequestInterceptor {
public void apply(RequestTemplate requestTemplate) {
// 统一设置请求头
requestTemplate.header("source", "wfx-order1");
}
}6. Sentinel 针对来源
流控规则中的"针对来源"同样依赖 RequestOriginParser 解析出的来源标识,可以对指定来源单独限流:

package com.wfx.sentinel;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* <p>title: com.wfx.sentinel</p>
* author zhuximing
* description: 解析请求来源
*/
@Component
public class CustomerRequestOriginParser implements RequestOriginParser {
public String parseOrigin(HttpServletRequest httpServletRequest) {
String source = httpServletRequest.getHeader("source");
System.out.println("come in");
System.out.println("source" + source);
if (source != null) {
return source;
}
return null;
}
}7. 系统规则
系统自适应限流从整体维度对入口流量进行控制,包含以下策略:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt估算得出。设定参考值一般是CPU cores * 2.5; - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏;
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒;
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护;
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
8. @SentinelResource 注解
8.1 流控自定义异常处理
// 主逻辑
@RequestMapping("test9")
@SentinelResource(value = "test9", blockHandler = "test9Handler")
public String test9(String name) {
// todo ...
return "test9";
}
// 备选逻辑:处理BlockException(流控、熔断等sentinel拦截)
public String test9Handler(String name, BlockException exception) {
return "系统繁忙,请稍后重试!!!";
}8.2 流控自定义异常优化
备选逻辑较多时,可以单独放到一个类里,用 blockHandlerClass 指定(方法必须是静态方法)。
主逻辑:
// 核心主逻辑
@GetMapping("test-d")
@SentinelResource(value = "test-d", blockHandlerClass = CustomBlockHandler.class, blockHandler = "testblockHandler")
public Map test(String name) {
return new HashMap() {{
put("code", "200");
put("msg", "success");
}};
}降级逻辑类:
package com.wfx.blockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import java.util.HashMap;
import java.util.Map;
public class CustomBlockHandler {
// 降级逻辑(备选逻辑),BlockException参数必须写,方法定义为静态方法
public static Map testblockHandler(String name, BlockException exception) {
System.out.println(name);
return new HashMap() {{
put("code", "200");
put("msg", "系统繁忙,请稍后重试!!!!");
}};
}
}8.3 熔断降级自定义处理
blockHandler 处理的是 BlockException(sentinel 规则拦截),业务异常走的是 fallback:
// 主逻辑
@RequestMapping("test6")
@SentinelResource(value = "test6", fallback = "test6Handler")
public String test6(String flag) throws Exception {
if (flag == null) {
throw new IllegalArgumentException("参数异常");
}
return "test6";
}
// 备选逻辑
public String test6Handler(String flag, Throwable throwable) throws Exception {
if (throwable instanceof DegradeException) {
return "系统开小差,一会重试!!!";
} else {
return "非sentinel异常";
}
}8.4 熔断降级自定义处理优化
主逻辑:
// 主逻辑
@RequestMapping("test")
@SentinelResource(value = "test", fallback = "testFallback", fallbackClass = HandleFallback.class)
public String test(Integer flag) {
System.out.println(">>>>>>>>>>>>>>>>>>");
if (flag == 0) {
throw new IllegalArgumentException("参数异常");
}
return "test";
}降级逻辑类:
package com.wfx.sentinel;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
/**
* <p>title: com.wfx.sentinel</p>
* author zhuximing
* description: 集中管理降级逻辑
*/
public class HandleFallback {
// 降级逻辑,方法定义为静态方法
public static String testFallback(Integer flag, Throwable throwable) {
if (throwable instanceof DegradeException) {
return "触发了熔断系统开了个玩笑!!!";
} else {
return "其他异常系统开了个玩笑!!!";
}
}
}8.5 全局异常处理
不想逐个写备选逻辑时,也可以用 SpringBoot 的全局异常处理统一兜底:
package com.qf.exception;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* <p>title: com.qf.exception</p>
* <p>Company: wendao</p>
* author zhuximing
* date 2021/8/19
* description: sentinel异常的全局统一处理
*/
@ControllerAdvice
public class CustomerExceptionHandler {
// 流控规则,全局异常处理
@ExceptionHandler(FlowException.class)
@ResponseBody
public Map handlerFlowException() {
return new HashMap() {{
put("code", "5xx");
put("msg", "系统繁忙,稍后重试!!");
}};
}
// 熔断降级规则,全局异常处理
@ExceptionHandler(DegradeException.class)
@ResponseBody
public Map handlerDegradeException() {
return new HashMap() {{
put("code", "5xx");
put("msg", "系统开小差,稍后重试!!");
}};
}
// 权限规则,全局异常处理
@ExceptionHandler(AuthorityException.class)
@ResponseBody
public Map handlerAuthorityException() {
return new HashMap() {{
put("code", "5xx");
put("msg", "没有权限访问!!");
}};
}
}9. Sentinel 规则持久化
9.1 原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中。
这种做法的好处是简单、无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。

9.2 Pull 模式
FileRefreshableDataSource定时从指定文件中读取规则 JSON 文件【图中的本地文件】,如果发现文件发生变化,就更新规则缓存;FileWritableDataSource接收控制台规则推送,并根据配置,修改规则 JSON 文件【图中的本地文件】。

9.2.1 pom 依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>9.2.2 核心代码
package com.qf.sentinel;
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* 拉模式规则持久化
*/
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
String ruleDir = System.getProperty("user.dir") + "/sentinel/rules";
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
authorityRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
private void mkdirIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}

9.2.3 配置
在项目的 resources/META-INF/services 目录下创建文件。
文件名:com.alibaba.csp.sentinel.init.InitFunc
内容为如下:
# 改成上面FileDataSourceInit的包名类名全路径即可
com.wfx.config.FileDataSourceInit9.2.4 优缺点分析
- 优点:
- 简单易懂;
- 没有多余依赖(比如配置中心、缓存等)。
- 缺点:
- 由于规则是用 FileRefreshableDataSource 定时更新的,所以规则更新会有延迟。如果 FileRefreshableDataSource 定时时间过大,可能长时间延迟;如果过小,又会影响性能;
- 规则存储在本地文件,如果有一天需要迁移微服务,那么需要把规则文件一起迁移,否则规则会丢失。
9.3 Push 模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper、Nacos、Apollo 等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台 / Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了。
9.3.1 控制台改造
直接使用改造好的控制台懒人包。
9.3.2 微服务端
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel的nacos持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>9.3.3 添加配置
spring:
cloud:
sentinel:
transport:
port: 8721
dashboard: localhost:8888
eager: true # 饥饿加载
web-context-unify: false
datasource:
flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-flow-rules
rule-type: flow
degrade:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-degrade-rules
rule-type: degrade
param-flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-param-rules
rule-type: param-flow
system:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-system-rules
rule-type: system
authority:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-authority-rules
rule-type: authority
nacos:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: sentinel10. 小结
- 服务雪崩的三类解法:限流、仓壁隔离、断路器,Sentinel 都有落地;
- Sentinel 与 Hystrix 相比:更轻(信号量隔离)、功能更全(QPS/并发限流、流量整形、系统保护、开箱即用的控制台),且 Hystrix 已停更;
- 流控:阈值类型 QPS / 线程数,流控手段直接、关联、链路,流控效果快速失败、Warm Up 预热、匀速排队(漏桶);
- 熔断降级三种策略:慢调用比例、异常比例、异常数;断路器状态机 Open -> Half-Open -> Close;
- 其他规则:热点参数规则(参数值级别阈值)、授权规则(RequestOriginParser 解析来源)、系统自适应规则;
@SentinelResource:blockHandler处理规则拦截,fallback处理业务异常,也可全局异常处理统一兜底;- 生产环境规则要持久化:Pull 模式(本地文件,简单但有延迟)、Push 模式(Nacos 等配置中心,生产推荐)。
