Sentinel介绍
1. 初识熔断和限流
在互联⽹应⽤中, 会有很多突发性的⾼并发访问场景。这些场景的特点是访 问量会突增,远远超出系统所能处理的并发数, 如果系统没有有效的保护机制, 所有流量都进⼊服务器, 很可能造成服务器宕机, 从⽽造成巨⼤损失。
为了保证系统的稳定性和可⽤性, 采取⼀定的系统保护策略变得⾄关重要. 其中, 服务限流和服务熔断是 两种常⻅的策略.
1.1 熔断
熔断机制,就类似于电力系统中的保险丝,当电流超过保险丝的载荷的时候,保险丝就会断开。在分布式系统中,服务之间的依赖非常常见,当某个服务不可用时,为了确保不会将影响扩散到整个系统,造成雪崩,熔断机制起到了有效的隔离措施。
1.2 限流
限流, 就是限制流量的意思。
2. Sentinel介绍
Sentinel 是由阿⾥巴巴开源的⼀个⾯向分布式、多语⾔异构化服务架构的流量治理组件. 主要以流量为 切⼊点,从流量路由、流量控制、流量整形、熔断降级、系统⾃适应过载保护、热点流量防护等多个 维度来帮助开发者保障微服务的稳定性。
2.1 Sentinel的特性
1. 丰富的应⽤场景 阿⾥巴巴 10 年双⼗⼀积累的丰富流量场景,包括秒杀、双⼗⼀零点持续洪峰、热点商品探测、预热、 消息队列削峰填⾕等多样化的场景。
2. 易于使⽤, 快速接⼊ 简单易⽤, 开源⽣态⼴泛, 针对 Dubbo、Spring Cloud、gRPC、Zuul、Reactor、Quarkus 等框架只需 要引⼊适配模块即可快速接⼊。
3. 多样化的流量控制 资源粒度、调⽤关系、指标类型、控制效果等多维度的流量控制。
4. 可视化的监控和规则管理 Sentinel提供了简单易⽤的 Sentinel 控制台, 开发者可以在控制台中看到接⼊的应⽤流量, 以及配置限 流规则等。
2.2 Sentinel组成
Sentinel分为两个部分:
1. 核⼼库(Java 客⼾端) : 不依赖任何框架/库, 能够运⾏于所有 Java 运⾏时环境, 同时对 Dubbo / Spring Cloud 等框架也有较好的⽀持。
2. 控制台(Dashboard) 基于 Spring Boot 开发, 打包后可以直接运⾏, 不需要额外的 Tomcat 等应⽤容 器。
2.3 Sentinel Dashboard下载和部署
1.下载
下载地址: https://github.com/alibaba/Sentinel/releases
2. 启动Sentinel Dashboard
把下载的jar放在⼀个⽬录下, 通过cmd 启动jar。
使⽤cmd, 启动命令
java -jar .\sentinel-dashboard-1.8.8.jar

显示这个,启动成功
访问: http://127.0.0.1:8080 默认的账号密码为sentinel

修改端⼝号和账号密码
java -jar -Dserver.port=8100
-Dsentinel.dashboard.auth.username=admin
-Dsentinel.dashboard.auth.password=admin
-Dserver.servlet.session.timeout=1440m
sentinel-dashboard-1.8.8.jar
这个配置为指定启动的端口号为8100,账号和密码都改为admin
指定 Spring Boot 服务端 session 的过期时间180分钟
更多配置参考: https://sentinelguard.io/zh-cn/docs/dashboard.html #控制台配置项模块

这样,我们的Sentinel Dashboard就部署成功了。
3. Sentinel 快速上手
Sentine的核心功能就是流量控制,和熔断降级。
那么如何实现呢?主要分下面几步:
1. 添加依赖
2. 定义资源
3. 定义限流规则
4. 检验规则是否⽣效
1. 创建空的maven项目
2. 添加依赖
添加Sentinel核⼼库
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
3. 定义资源
资源是 Sentinel 的关键概念, 被Sentinel监控的每个接⼝就是⼀个资源. 它可以是 Java 应⽤程序中的任 何内容, 例如, 由应⽤程序提供的服务, 或由应⽤程序调⽤的其它应⽤提供的服务, 甚⾄可以是⼀段代码. 限流, 熔断等都是针对资源来设置的。
public static void main(String[] args) {
// 配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
}
这个while是一个死循环,会一直判断是否满足规则,如果没有满足规则,那么就执行被保护的逻辑,如果满足规则了,就会执行catch中的逻辑
4.定义规则
请求是一秒钟的请求是20个超过20个就会进行限制
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //现在的请求是一秒钟的请求是20个超过20个就会进行限制
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
5启动查看日志
启动这个main方法,进入了循环

找到打印的日志:默认的日志路径在C:\Users\lucg\logs\csp中
打印的日志如下:


timestamp:是时间戳 datetime是时间 resource是资源名称
4. Spring Cloud 集成Sentinel
4.1 项⽬准备
使⽤Spring Cloud Gataway开发之后的项⽬ (这个项⽬包含服务注册/发现, 服务调⽤, 负载均衡等, ⽅便后⾯功能演示)。
4.2 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
4.3 配置sentinel控制台
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8100 #sentinel控制台地址
现在sentinel就配置成功了,当访问被配置的服务的时候,就可以在工作台看到访问记录
4.4 访问任意接⼝, 触发sentinel监控


簇点链路: 是指在微服务架构中, 请求从进⼊服务到处理完成所经过的完整调⽤链路. 当请求进⼊服务 时, ⾸先会访问DispatcherServlet, 然后进⼊Controller, Service, Mapper, 这样的⼀个调⽤链就叫做簇 点链路。
默认情况下, Sentinel starter会为Spring MVC的所有HTTP服务提供限流埋点, 所以如果只想对HTTP服 务进⾏限流, 那么只需要添加依赖即可, 不需要修改任何代码. 如果想要对特定的⽅法进⾏限流或者降 级, 则可以⾃定义资源来实现。
流控, 熔断等都是针对簇点链路中的资源来设置的, 因此我们可以点击对应资源后⾯的按钮来设置规 则:
• 流控:流量控制
• 熔断:服务熔断
• 热点:热点参数限流, 是限流的⼀种
• 授权:请求的权限控制
5. 流量控制
5.1 配置流控规则
点击资源右边的按钮[流控] 进⾏流量控制配置

qps超过1,就进行限流

限流的响应
也可以使⽤Jmeter来测试.
5.2 基于QPS/并发数的流量控制
流量控制主要有两种统计类型: ⼀种是统计线程数, 另外⼀种则是统计 QPS

5.2.1 并发线程数
线程数限流⽤于保护业务线程数不被耗尽
5.2.2 QPS流量控制
当 QPS 超过某个阈值的时候, 则进⾏流量控制.
流量控制的⼿段分为三种: 快速失败, Warm Up, 排队等待.
5.3 流控效果
流量超过配置的阈值时, 会采⽤流量控制, 流量控制的⼿段分为三种:

对应对应 FlowRule 中的 controlBehavior 字段. 取值分别为:
• 快速失败( RuleConstant.CONTROL_BEHVIOR_DEFAULT )
• Warm Up( RuleConstant.CONTROL_BEHAVIOR_WARM_UP )
• 排队等待( RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER )
5.3.1 快速失败
快速失败, 也就是直接拒绝, 对应 RuleConstant.CONTROL_BEHAVIOR_DEFAULT . 该⽅式是默认 的流量控制⽅式,当QPS超过任意规则的阈值后,新的请求就会被⽴即拒绝,拒绝⽅式为抛出 FlowException 。这种⽅式适⽤于对系统处理能⼒确切已知的情况下,⽐如通过压测确定了系统的 准确⽔位. 上⾯的例⼦, 使⽤的就是快速失败.
5.3.2 Warm Up
Warm up, 对应 RuleConstant.CONTROL_BEHAVIOR_WARM_UP . Warm Up 也叫预热模式. 阈值 ⼀般是⼀个微服务能承受的最⼤QPS, 但是⼀个服务刚刚启动时, ⼀切资源尚未初始化, 如果直接将QPS 跑到最⼤值, 可能导致服务瞬间宕机.
该⽅式主要⽤于系统⻓期处于低⽔位的情况下,当流量突然增加时, 直接把系统拉升到⾼⽔位可能瞬间 把系统压垮的, 通过"冷启动",让通过的流量缓慢增加,在⼀定时间内逐渐增加到阈值上限,给冷系统 ⼀个预热的时间,避免冷系统被压垮的情况.

预热时长:也就是说系统冷启动,在多长时间内,系统可以承受设置的qps
例如:设置的qps的阈值是10.预热时间五秒,那么它不会一下子给上十个,会在五秒后逐渐增长到10。

我们用jmeter来实验:发送一百个请求,用十秒的时间:

执行结果如下:

可以看出,拒绝的数量在降低,通过的数量在升高,最后达到每秒十个通过量。
5.3.3 排队等待
排队等待,这种⽅式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过. 可以理解为让所有请求进⼊⼀个队列中, 然后按 照阈值允许的时间间隔依次执⾏. 后⾯的请求必须等待前⾯执⾏完成, 直到超时。
例如:如果qps是10的话:就是没秒处理十个请求,就算在一瞬间来了十个请求的话,也要平均分到每100ms来处理一个请求。这种方式主要用于处理间隔性的突发流量,例如消息队列。
可以想一下,在某一秒中有量的请求到来,接下来几秒就会处于空闲,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是第一秒直接拒绝多余的请求。
例:现在qps是1,每秒一个请求,超时时间是5秒钟。
假设同时发起10个请求
快速失败:通过一个拒绝九个
排队等待:第一个请求等待时间是0,第二个请求等待一秒,第三个请求等待两秒...........
第六个请求等待五秒。
第七个请求需要等待6秒,超出了等待时间,那么就会拒绝这个请求,拒绝第七个到第十个请求


实验结果:


拒绝了四个请求,通过了六个请求。
5.4 流控模式
在添加限流规则时, 点击⾼级选项, 可以发现流控模式分为三种: 直接, 关联, 链路, 接下来我们详细讲⼀ 下这三种模式.

调⽤关系包括调⽤⽅, 和被调⽤⽅. ⽅法⼜可能会调⽤其它⽅法, 形成⼀个调⽤链路的层次关系. Sentinel记录资源之间的调⽤链路, 这些资源通过调⽤关系, 相互之间构成⼀棵调⽤树.
Sentinel根据这些调⽤关系, 建⽴不同资源间的调⽤关系, 并记录每个资源的实时统计信息. 有了调⽤链路的统计信息,我们可以衍⽣出多种流量控制⼿段.
5.4.1 根据调用方限流
也就是上述流控模式中的: [直接] ,这也是默认的流量控制⽅式.当超出阈值的时候,对当前资源进行限流
针对来源:default,表示不区分调用者,来自任何调用者的请求都将进行限流统计,如果这个资源调用总和超出阈值,那么就会触发限流。
5.4.2 根据调⽤链路⼊⼝限流
也就是上述流控模式中的: [链路]
链路限流是指: 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流。
例:写两个接口共同调用一个资源
@RequestMapping("/write")
public String write(){
System.out.println("写操作");
orderService.queryOrder();
return "写操作";
}
@RequestMapping("/read")
public String read(){
System.out.println("读操作");
orderService.queryOrder();
return "读操作";
}
@SentinelResource("queryOrder") //这个注解就是把这个方法定义一个资源,参数是资源名称
public void queryOrder(){
System.out.println("查询订单信息");
return;
}
分别请求这两个接口

有两个链路,为什么第二个下面没有queryOrder这个资源呢?
是因为,链路模式中, 是对不同来源的两个链路做监控. 但是sentinel默认会给进⼊SpringMVC的所有请求设置同 ⼀个root(sentinel_spring_web_context)资源, 会导致链路模式失效. 我们需要关闭这种对SpringMVC的资源聚合, 修改配置如下:
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8100 #sentinel控制台地址
web-context-unify: false #关闭context整合
重启服务:关闭整合成功,就可以看到链路的关系了

设置流控规则:
只针对⼊⼝资源为 /order/read 的请求进⾏限流

创建测试任务
创建线程组, 添加两个HTTP取样器, 分别对应接⼝为 /order/wirte 和 /order/read



得出结论:write链路中没有限流,read链路请求被限流。故,根据链路访问到资源的请求,触发阈值的时候,对指定链路限流。
5.4.3 具有关系的资源流量控制
当两个资源之间具有资源争抢或者依赖关系的时候, 这两个资源便具有了关联.
关联限流就是 统计与当前资源相关的另⼀个资源, 触发阈值时, 对当前资源限流.

例如:

当写的阈值超过5的时候,就会对读进行限流。

用jmeter进行测试两秒发出20个请求


结果:写的请求正常,读被限流了。当write资源请求超过5时,对read资源进行限流
应⽤场景:
• 在数据库操作中, 读操作和写操作可能会争抢资源(如锁). 通过关联模式, 可以限制读操作的频率, 以 避免对写操作造成过⼤的影响.
• 订单服务可能依赖于库存服务, 如果库存服务的请求量过⼤, 可以通过关联模式限制订单服务的请求 量.
6. 热点参数限流
上面提到的流控规则:是针对某一个接口/资源 进行规则配置,不区分参数,所有的请求参数⼀视同仁, 只要达到阈值, 就⼀起限流。
热点:也就是经常访问的数据。热点参数限流会统计传⼊参数中的热点参数, 并根据配置的限流阈值与模式, 对包含热点参数的资源调 ⽤进⾏限流. 热点参数限流可以看做是⼀种特殊的流量控制, 仅对包含热点参数的资源调⽤⽣效.
可以理解成,id为1阈值为10,id为2阈值是5。每一个参数的阈值不同。
热点参数限流是以资源参数值为维度统计的。
流控是以资源名为维度来统计的
热点参数限流配置
定义资源:热点参数限流对默认的Spring MVC 资源⽆效, 在资源上需要加 @SentinelResource
@SentinelResource("/sentinel/id")
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
return orderService.selectOrderById(orderId);
}
设置热点参数限流 在需要进⾏热点参数限流的资源后⾯, 点击 [+热点]

参数索引:就是热点资源,需要接收参数,参数为1~n,配置对第几个参数进行热点参数限流。传入的参数从0开始
统计窗口时长:就是每秒钟相同参数qps超过5则进行限流


启动运行结果:



每秒中放行了5个
参数例外项
上述的热点参数配置中, 所有的参数⼀视同仁, QPS都被限定为5, 但是在⼀些场景下, 我们希望可以对某 些热点参数进⾏单独限流.
但是在⼀些场景下, 有⼀些热点商品, ⽐如排名⽐较靠前的, 访问次数⽐较多, 我们希望可以对这个热点 商品进⾏单独限流, 让他的阈值⾼⼀些或者低⼀些, 就需要使⽤到热点参数限流规则⾥⾯的⾼级选项了.
在热点规则中点击编辑热点,会出现一个高级选项

参数类型为选择int类型
当参数为1的时候阈值为8参数为2的时候阈值为15
参数例外项上面设置的是:所有参数,单机qps阈值达到5时,则进行限流
例外项目:不遵守上面的规则,遵守例外设置的规则

启动后:结果为id为1的每秒超过8个限流。id2的每秒超过15进行限流。id3的每秒超过5个限流
7. 限流算法
我们学习了使⽤Sentinel完成服务的限流, 要实现服务限流, 最重要的就是限流算法, 下⾯来简单介绍下 常⻅的限流实现算法.
常见的限流算法:
1.计数器算法/固定窗口算法
2.滑动窗口算法
3.漏桶算法
4.令牌桶算法
7.1 计数器算法
计数器算法, 也叫固定窗⼝限流算法, 这是⼀种⽐较简单的限流实现算法.
⾸先维护⼀个计数器, 在指定周期内, 累加访问次数, 当访问次数达到设定的阈值时, 触发限流策略, 当进 ⼊下⼀个时间周期时, 将访问次数清零. 这个时间周期, 就可以理解为⼀个窗⼝, 计数器记录这个窗⼝接 收请求的次数.
例如:设定一个一分钟阈值为100的限流,那么在0秒开始计数,来一个请求计数器就加1,到第60秒的时候计数器清零,重新计数。
还有一个场景:在第0-58秒之间请求量为0,但是在第58秒到六十秒的请求量为100,在60到62秒的请求量为100,那么在这短短的四秒请求量为200。远远超越了服务处理能力,可能就此使服务崩溃。
解决这一问题:滑动窗口算法。
7.2 滑动窗⼝算法
为了解决计数器算法带来的临界问题, 所以引⼊了滑动窗⼝算法. 滑动窗⼝是⼀种流量控制技术, 在TCP ⽹络通信协议中, 就采⽤了滑动窗⼝算法来解决⽹络拥塞的情况.
简单来说, 滑动窗⼝算法的原理是在固定窗⼝中分割出多个⼩时间窗⼝, 分别在每个⼩时间窗⼝中记录 访问次数,然后根据时间将窗⼝往前滑动并删除过期的⼩时间窗⼝. 最终只需要统计滑动窗⼝范围内的 所有⼩时间窗⼝总的计数即可.
7.3 漏桶算法
漏桶算法⾯对限流, 就更加的柔和, 不存在直接的粗暴拒绝. 漏桶限流算法的主要作⽤是控制数据注⼊⽹ 络的速度, 平滑⽹络上的突发流量.
它的原理很简单, 可以认为就是注⽔漏⽔的过程, 往漏桶中以任意速率流⼊⽔, 以固定的速率流出⽔. 当 ⽔超过桶的容量时, 会被溢出, 也就是被丢弃. 因为桶容量是不变的, 保证了整体的速率.
漏桶算法的原理, 也就决定着, 使⽤漏桶算法会有以下缺点:
1. ⽆法处理突发流量: 漏桶算法以固定的速率处理请求, 当流量突然增加时, ⽆法快速响应和处理这些 请求. 超出漏桶容量的请求会被丢弃, 这可能导致⽤⼾体验下降.
2. 可能导致请求延迟: 由于漏桶的流出速率是固定的, 即使在流量较⼩的情况下, 请求也需要排队等待 处理, 这可能导致请求的响应时间变⻓.
这些缺点使得漏桶算法在某些需要快速响应或处理突发流量的场景中可能不是最佳选择, 其他限流算法 如令牌桶算法可能更适合处理复杂多变的流量场景.
7.4 令牌桶算法
令牌桶是⽹络流量整形和速率限制中最常使⽤的⼀种算法. 对于每⼀个请求, 都需要从令牌桶中获得⼀ 个令牌, 如果没有获得令牌, 则触发限流策略.

如图所⽰,系统会以⼀个恒定速度往固定容量的令牌桶中放⼊令牌, 如果此时有客⼾端请求过来, 则需 要先从令牌桶中拿到令牌以获得访问资格.
假设令牌⽣成速度是每秒10个, 也就等同于QPS=10, 在请求获取令牌的时候, 会存在三种情况:
1. 请求速度 > 令牌⽣成速度: 令牌会很快被取完, 后续再进来的请求会被限流.
2. 请求速度 = 令牌⽣成速度: 流量处于平稳状态.
3. 请求速度 < 令牌⽣成速度: 说明系统的并发数不⾼, 请求能被正常处理.
由于令牌桶有固定的⼤⼩, 当请求速度⼩于令牌⽣成速度时, 令牌桶会被填满. 所以令牌桶能够处理突发 流量. 也就是在短时间内新增的流量系统能够正常处理, 这是令牌桶的特性。
8. 熔断降级
除了流量控制以外,对调⽤链路中不稳定的资源进⾏熔断降级也是保障⾼可⽤的重要措施之⼀.现代微服务架构都是分布式的,由⾮常多的服务组成.不同服务之间相互调⽤,组成复杂的调⽤链路.复 杂链路上的某⼀环不稳定,就可能会层层级联,最终导致整个链路都不可⽤.因此我们需要对不稳定的弱 依赖服务调⽤进⾏熔断,暂时切断不稳定调⽤,避免局部不稳定因素导致整体的雪崩.
熔断降级作为保护⾃⾝的⼿段,通常在客⼾端(调⽤端)进⾏配置.
Sentinel 提供了三种熔断策略:慢调⽤,异常⽐例,异常数。
• 慢调⽤⽐例( SLOW_REQUEST_RATIO ):需要设置允许的慢调⽤RT(即最⼤的响应时间),请求的 响应时间⼤于该值则统计为慢调⽤.在指定时间内,如果请求数量>设定的最⼩数量,且慢调⽤⽐例 > 设定的阈值,则触发熔断
• 异常⽐例( ERROR_RATIO ):指定时间内,请求数量>设置的最⼩数量,且异常⽐例⼤于阈值,则触 发熔断
• 异常数( ERROR_COUNT ):指定时间内,异常数⽬超过阈值后,则触发熔断.

8.1 状态机
熔断的思路是由断路器(或者叫熔断器)统计服务调⽤的慢请求⽐例,异常⽐例等,如果超过阈值,则熔断 该服务,也就是拦截对该服务的请求,当服务恢复时,断路器会放⾏访问该服务的请求.
断路器控制熔断和放⾏是通过状态机来完成的

状态机有三个状态:
• Closed:关闭状态,所有请求都会通过断路器,并开始统计慢请求⽐例,异常⽐例,超过阈值则切换到 open状态.
• Open:打开状态,服务调⽤被熔断.这时所有访问被熔断服务的请求都会被拒绝.
• Half-open:半开状态,当经过⼀段时间后,断路器会从Open状态切换到Half-open状态,这时会有⼀ 定数量的请求被放⼊,根据这些请求的失败率来判断后续操作.
◦ 失败率低于阈值:切换到closed状态
◦ 失败率超过阈值:切换到open状态
8.2 慢调⽤⽐例
8.2.1 模拟慢调⽤
模拟product-service慢响应
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId){
log.info("接收参数, productId:"+productId);
try {
long time=new Random().nextInt(20)+50;//随机生成一个50到70之间的数字
Thread.sleep(time);//随机休眠50到70毫秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return productService.selectProductById(productId);
}
8.2.2 定义资源
为了方便配置熔断规则,在调用服务的方法上定义一个资源@SentinelResource("selectOrderById")
@SentinelResource("selectOrderById")
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
ProductInfo productInfo = productApi.getProductInfo(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
8.2.3 配置熔断规则

:超过50ms的调⽤是慢调⽤,统计最近10000内的请求,如果请求量超过5次,并且慢调⽤⽐ 例不低于0.5,则触发熔断.熔断时⻓为5s,然后进⼊half-open状态,放⾏⼀次请求做测试
8.2.4 验证熔断结果
连续点击发送请求后被熔断

8.3 降级
当调⽤失败后,业务直接报错,给⽤⼾体验不太好,应该返回⽤⼾⼀个友好提⽰或者默认结果,这个就是 降级.⽐如获取⽤⼾信息时,照⽚获取失败,返回⼀个默认的头像
通常有以下⽅式:
8.3.1 捕获异常
对资源代码进⾏异常捕获,当发⽣熔断时,返回空对象
@SentinelResource("/sentinel/id")
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
try{
OrderInfo orderInfo=orderService.selectOrderById(orderId);
return orderInfo;
}catch (UndeclaredThrowableException e){
log.error("获取订单详情失败");
return new OrderInfo(); //返回了一个空的对象
}
}

8.3.2 FallbackFactory
FallbackFactory 是⼀个在微服务架构中⽤于实现服务降级的接⼝,当远程服务调⽤失败或超时, FallbackFactory 会根据提供的异常信息创建⼀个降级处理实例,以替代原服务调⽤.并且可以根 据错误类型,返回不同的降级响应.
定义降级逻辑



这个对象会在order服务启动的时候被注入

在order-service修改配置,开启 feign 对 Sentinel 功能
feign:
sentinel:
enabled: true #开启feign对sentinel的支持
配置熔断规则

验证熔断结果 快速访问接⼝,触发熔断,会发现远程返回的内容为FallbackFactory设置的接⼝内容.

验证限流结果 当触发限流时,也会执⾏FallbackFactory预设的逻辑
FallbackFactory它对限流和熔断都是有效的。
8.4 异常⽐例
异常⽐例( ERROR_RATIO ):统计单位时⻓( statIntervalMs )内,请求数⽬⼤于最⼩请求数 ⽬,并且异常的⽐例⼤于阈值,则触发熔断..经过熔断时⻓后熔 断器会进⼊探测恢复状态(HALF-OPEN状态),若接下来的⼀个请求成功完成(没有错误)则结束熔 断,否则会再次被熔断.
8.4.1模拟异常
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) throws InterruptedException {
log.info("接收参数, productId:"+productId);
if(productId==1001){ //慢调用
Thread.sleep(60);
return productService.selectProductById(productId);
} else if (productId==1002) { //发生异常
throw new RuntimeException("发生异常");
}else { //正常执行
return productService.selectProductById(productId);
}
}
8.4.2 配置熔断规则

8.4.3 验证熔断结果
未触发熔断
快速多次请求:http://127.0.0.1:8080/order/1, (对应产品ID1001)⼀直不会熔断。
⼀次请求:http://127.0.0.1:8080/order/2, (对应产品ID1002), 出现异常,⾛降级逻辑。
再请求http://127.0.0.1:8080/order/1, 正常返回。
触发熔断
快速多次请求 http://127.0.0.1:8080/order/2,异常⽐例达到阈值,触发熔断。.经过熔断时⻓后熔 断器会进⼊探测恢复状态(HALF-OPEN状态),若接下来的⼀个请求成功完成(没有错误)则结束熔 断,否则会再次被熔断.
再请求http://127.0.0.1:8080/order/1, ⾛降级逻辑。
8.5 异常数
异常数( ERROR_COUNT ):统计单位时⻓内的异常数⽬,超过阈值之后,触发熔断.
熔断规则配置如下: 统计最近10000ms内的请求,如果请求量超过5次,并且⼤于异常数,则触发熔断.

9. 授权规则
现在服务中存在的问题:比如网关服务现在调用了订单服务,订单服务调用了商品服务
在浏览器中,可以直接调用订单服务或者是商品服务。那么这些服务可以让人随便调用的话是不安全的,想要通过网关服务来调用其他服务,就要对网关进行授权

Sentinel提供了两种授权模式:⽩名单和⿊名单
• 白名单:请求来源位于白名单内的调⽤者才允许访问.
• 黑名单:请求来源位于黑名单内的调⽤者不允许访问,其余请求通过.
Sentinel根据来源来进⾏判断, 所以调⽤⽅需要设置来源, 被调⽤⽅需要获取来源 Sentinel进⾏授权管理, 主要分以下⼏步
9.1 服务端(被调⽤⽅)获取来源
服务端也就是order-service项⽬.
Sentinel是通过 RequestOriginParser 这个接⼝的 parseOrigin 来获取请求的来源的. 在服务端中实现RequestOriginParser这个接⼝即可
定义请求来源放在Header中, key为origin(⾃定义)

创建一个类HeaderOriginParser继承RequestOriginParser这个接口
这个方法返回的String类型的对象就是设置的来源
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
//获取来源
String origin = httpServletRequest.getHeader("origin"); //请求的来源会放在header中,是key-value的来源的key是origin
if(!StringUtils.hasLength(origin)){
return "default";
}
return origin;
}
}
9.2 客⼾端(调⽤⽅)设置来源
也就是gateway项⽬ 可以通过AddRequestHeader Filter设置Header
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway
9.3 配置授权规则

再去访问后

会被限制访问

网关服务访问成功
10. 定义异常返回结果

当授权未被通过的时候,和限流返回的是一样的,这样会让调用放分辨不出来是什么异常原因
Sentinel提供了⼀个接⼝ BlockExceptionHandler , ⽤于⾃定义处理 BlockException 异常. 当请求被 Sentinel 限流、降级或授权拒绝时, 会抛出 BlockException . 通过实现 BlockExceptionHandler 接⼝, 可以定义统⼀的异常处理逻辑, 返回更友好的错误信息或执⾏特 定的降级操作.
默认处理
Sentinel 默认提供了⼀个 DefaultBlockExceptionHandler 实现类,当返回被堵塞的时候会默认返回这个字符串。
"Blocked by Sentinel (flow limiting)"

想要自定义异常处理,只需要实现这个接口即可

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
//系统默认的方法返回的是纯英文,所以不用默认编码,我们要是返回的中文要设置编码格式
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
int status=429;
String msg="Blocked by Sentinel (flow limiting)"; //返回的状态码默认429 返回的异常信息默认的是这个
if(e instanceof AuthorityException){ //如果发生的异常是授权失败的时候
msg="授权失败,请联系服务端进行配置";
} else if (e instanceof DegradeException) {
msg="触发降级规则,请联系服务端进行配置";
}else if(e instanceof FlowException){
msg="触发限流规则,请联系服务端进行配置";
}else if(e instanceof ParamFlowException){
msg="触发热点限流规则,请联系服务端进行配置";
}
response.setStatus(status);
out.print(msg);
out.flush();
out.close();
}
}
验证结果
触发授权规则的时候

11. 规则管理及推送
我们也发现了⼀个问题, 就是服务重启后, 我们之前设定的规则就会消失.这是因为 Sentinel 默认是将这些管理规则保存在内存中, 在⽣产环境中, 这个问题是不可接受的. 规则的丢失会导 致系统失去流量控制和保护机制.
⽣产环境中, Sentinel需要规则持久化, sentinel-core 提供 API 和扩展接⼝来接收信息. 开发者需 要根据⾃⼰的环境, 选取⼀个可靠的推送规则⽅式. 同时, 规则最好在控制台中集中管理.
11.1 原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中

这种做法的好处是简单,⽆依赖;坏处是应⽤重启规则就会消失,仅⽤于简单测试,不能⽤于⽣产环境
11.2 Pull模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。

以本地文件数据源为例:
1. 注册数据源
package com.bite.order.sentinel;
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.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
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 {
// 这个是或取当前用户的目录,你可以先获取当前项目的目录
//
// String rulePath=System.getProperty("user.dir")+"/sentinel/rules/orderService"; //拿到路径
String rulePath="E:\\home"+"/sentinel/rules/orderService";
//流控文件的路径
String flowRulePath = rulePath+"/flow-rule.json";
//如果路径不存在的话
mkdirIfDirExist(rulePath);
//如果文件按不存在的话
creatFileIfExist(flowRulePath);
//在文件中读取规则到ReadableDataSource<String, List<FlowRule>> ds
ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
);
// 将可读数据源注册至 FlowRuleManager.
FlowRuleManager.register2Property(ds.getProperty());
WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
WritableDataSourceRegistry.registerFlowDataSource(wds);
}
private void creatFileIfExist(String filePath) throws IOException {
File file=new File(filePath);
if(!file.exists()){
file.createNewFile();
}
}
private void mkdirIfDirExist(String rulePath) {
File file=new File(rulePath);
if(!file.exists()){
file.mkdirs();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}

2.在resources下创建META-INF/services⽬录,并创建⽂件: com.alibaba.csp.sentinel.init.InitFunc, 写本地数据源的路径

com.bite.order.sentinel.FileDataSourceInit
3. 重启服务, 设置流控, 会发现流控规则同步到指定的⽂件中.
4. 再次重启服务, 规则依然保存(需要请求⼀下, 规则才可以看到)
5. 修改配置⽂件, 可以看到控制台也得到了更新.
这种实现⽅法好处是简单,不引⼊新的依赖,坏处是⽆法保证监控数据的⼀致性。
11.3Push模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:

Sentinel官⽅提供了ZooKeeper, Redis, Nacos, Apollo, etcd 等的动态数据源实现, 参考: sentinel官方文档
推模式:使用 Nacos 配置规则
Nacos 是阿里中间件团队开源的服务发现和动态配置中心。Sentinel 针对 Nacos 作了适配,底层可以采用 Nacos 作为规则配置数据源。使用时只需添加以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>x.y.z</version>
</dependency>
注册数据源
若不希望手动注册数据源,可以借助 Sentinel 的 InitFunc SPI 扩展接口。只需要实现自己的 InitFunc 接口,在 init 方法中编写注册数据源的逻辑。比如:
package com.bite.order.sentinel;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.util.List;
public class NacosDatasourceInit implements InitFunc {
@Override
public void init() throws Exception {
// remoteAddress 代表 Nacos 服务端的地址
// groupId 和 dataId 对应 Nacos 中相应配置
final String remoteAddress = "49.232.195.59:10020";
final String groupId = "SENTINEL_GROUP";
final String dataId = "order-service-flow-rule";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
}


启动服务,在nacos配置中心配置。
验证结果 重启服务, 会发现Sentinel Dashboard已经从nacos获取规则配置信息了.
1. 修改Nacos⽂件, 发现Sentinel Dashboard 会及时更新
2. 通过Sentinel Dashboard修改规则, 发现Nacos配置⽂件并没有同步更新.
11.4 Sentinel集成Nacos持久化
上述我们发现, Nacos修改的内容会及时同步到Sentinel DashBoard, 但是Sentinel DashBoard更新的 内容, 并不会同步到Nacos, 这在⽣产环境中, 也是不⽅便使⽤的. 所以我们需要修改Sentinel的源码, 让 其⽀持双向通讯.
11.4.1 源码改造
1. 下载Sentinel源码
下载地址: https://github.com/alibaba/Sentinel/releases

2. 修改pom⽂件
解压, 使⽤idea打开, 修改pom⽂件 在sentinel-dashboard源码的pom⽂件中, nacos的依赖默认的scope是test, 只能在测试时使⽤
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!-- <scope>test</scope>-->
注掉 test
3. 添加Nacos⽀持
sentinel-dashboard 的 test 包下, 已经编写了对nacos的⽀持

把test中的文件挪到main中。
4. 修改Nacos配置

改成自己nacos的地址
5. 配置Nacos数据源

6. 修改前端⻚⾯
前端共3处修改


打开这个注释

把js中的v1修改成v2 已经标出

在这里加上/v2
7. 重新打包, 启动

11.4.2 配置Nacos
修改order-service 服务, 让其监听Nacos的规则配置
1. 引⼊依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2. 配置Nacos地址
sentinel:
transport:
dashboard: 127.0.0.1:8100 #sentinel控制台地址
web-context-unify: false #关闭context整合
datasource:
flow-rules: #流控规则
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
rule-type: flow
3. 验证
重启服务, 通过Sentinel Dashboard设置流控规则, 观察Nacos变化
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)