目录
- 一、熔断和限流
-
- [1.1 熔断](#1.1 熔断)
- [1.2 限流](#1.2 限流)
- [二、Sentinel 简介](#二、Sentinel 简介)
- 三、快速使用
- [四、spring cloud 集成 sentinel](#四、spring cloud 集成 sentinel)
- 五、流量控制
-
- [5.1 配置流控规则](#5.1 配置流控规则)
- [5.2 基于QPS/并发线程数的流量控制](#5.2 基于QPS/并发线程数的流量控制)
- [5.3 流控效果](#5.3 流控效果)
-
- [5.3.1 快速失败](#5.3.1 快速失败)
- [5.3.2 Warm Up](#5.3.2 Warm Up)
- [5.3.3 排队等待](#5.3.3 排队等待)
- [5.4 流控模式](#5.4 流控模式)
-
- [5.4.1 直接](#5.4.1 直接)
- [5.4.2 链路](#5.4.2 链路)
- [5.4.3 关联](#5.4.3 关联)
- 六、热点参数限流
-
- [6.1 热点参数限流配置](#6.1 热点参数限流配置)
- [6.2 参数例外项](#6.2 参数例外项)
- 七、限流算法
-
- [7.1 计数器算法](#7.1 计数器算法)
- [7.2 滑动窗⼝算法](#7.2 滑动窗⼝算法)
- [7.3 漏桶算法](#7.3 漏桶算法)
- [7.4 令牌桶算法](#7.4 令牌桶算法)
- 八、熔断降级
-
- [8.1 状态机](#8.1 状态机)
- [8.2 慢调用比例](#8.2 慢调用比例)
-
- [8.2.1 模拟慢调⽤](#8.2.1 模拟慢调⽤)
- [8.2.2 定义资源](#8.2.2 定义资源)
- [8.2.3 配置熔断规则](#8.2.3 配置熔断规则)
- [8.2.4 验证熔断结果](#8.2.4 验证熔断结果)
- [8.3 降级](#8.3 降级)
-
- [8.3.1 捕获异常](#8.3.1 捕获异常)
- [8.3.2 FallbackFactory](#8.3.2 FallbackFactory)
- [8.4 异常⽐例](#8.4 异常⽐例)
-
- [8.4.1 模拟异常](#8.4.1 模拟异常)
- [8.4.2 配置熔断规则](#8.4.2 配置熔断规则)
- [8.4.3 验证熔断结果](#8.4.3 验证熔断结果)
- [8.5 异常数](#8.5 异常数)
- 九、授权规则
-
- [9.1 服务端(被调⽤⽅)获取来源](#9.1 服务端(被调⽤⽅)获取来源)
- [9.2 客⼾端(调⽤⽅)设置来源](#9.2 客⼾端(调⽤⽅)设置来源)
- [9.3 配置授权规则](#9.3 配置授权规则)
- 十、定义异常返回结果
- 十一、管理规则与推送
-
- [11.1 原始模式](#11.1 原始模式)
- [11.2 Pull模式](#11.2 Pull模式)
- [11.3 Push模式](#11.3 Push模式)
-
- [11.3.1 11.3.1 Nacos官⽅⽀持](#11.3.1 11.3.1 Nacos官⽅⽀持)
- [11.4 Sentinel集成Nacos持久化](#11.4 Sentinel集成Nacos持久化)
-
- [11.4.1 源码改造](#11.4.1 源码改造)
- [11.4.2 配置Nacos](#11.4.2 配置Nacos)

一、熔断和限流
1.1 熔断
服务熔断机制类似于电⼒系统中的保险丝,当电流超过保险丝的承载能⼒时,保险丝会熔断以保护电路不受损害。在分布式系统中,服务间依赖⾮常常⻅,当某个服务不可⽤时,如果没有有效的隔离措施,故障可能会迅速扩散到整个系统,导致系统雪崩。

如上面图片中的调用链,当F发生故障的时候,C去调用F的时候会一直等到F响应超时,如果流量⽐较⼤,每个请求都等到超时才进⾏响应,那么系统C的线程资源很快就会被耗尽不能对外提供服务了(处理完成后,才会释放资源)。
服务熔断是指当某个服务⽆法正常为服务调⽤者提供服务时,⽐如请求超时,服务异常等,为了防⽌整个系统出现雪崩效应,暂时将出现故障的接⼝隔离出来,断绝与外部接⼝的联系,当触发熔断之后,后续⼀段时间内该服务调⽤者的请求都会直接失败(⽆需等待到超时),直到⽬标服务恢复正常。
熔断机制能够在服务故障时及时切断调⽤链,防⽌故障扩散,减少对故障服务的资源消耗,保护系统资源不被耗尽,从⽽保护系统的整体稳定性和可⽤性。
1.2 限流
限流:请求过多时,限制流量,在互联⽹应⽤中,限流可以确保服务器能够处理的请求数量在合理范围内,避免因请求过多导致服务响应慢或失败。可以保证⽤⼾在⾼流量时段仍然能够获得快速响应,提升⽤⼾体验。
二、Sentinel 简介
Sentinel 是由阿⾥巴巴开源的⼀个⾯向分布式、多语⾔异构化服务架构的流量治理组件。主要以流量为切⼊点,从流量路由、流量控制、流量整形、熔断降级、系统⾃适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel组成分为两个部分:
- 核⼼库(Java 客⼾端):不依赖任何框架/库,能够运⾏于所有 Java 运⾏时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的⽀持。
- 控制台(Dashboard) 基于 Spring Boot 开发,打包后可以直接运⾏,不需要额外的 Tomcat 等应⽤容器
Sentinel Dashboard下载和部署:
下载地址:https://github.com/alibaba/Sentinel/releases

使⽤cmd,启动命令:java -jar .\sentinel-dashboard-1.8.9.jar
默认⽤⼾名和密码都是:sentinel

配置参考:https://sentinelguard.io/zh-cn/docs/dashboard.html
三、快速使用
Sentinel 的核⼼功能是流量控制和熔断降级,我们先使⽤Sentinel核⼼库来实现限流。
使⽤Sentinel核⼼库来实现限流,主要分以下⼏个步骤
- 添加依赖
- 定义资源
- 定义限流规则
- 检验规则是否⽣效
创建空的maven项⽬:

- 添加依赖
添加Sentinel核⼼库
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
- 定义资源
资源是 Sentinel 的关键概念,被Sentinel监控的每个接⼝就是⼀个资源。它可以是 Java 应⽤程序中的任何内容,例如,由应⽤程序提供的服务,或由应⽤程序调⽤的其它应⽤提供的服务,甚⾄可以是⼀段代码。
限流,熔断等都是针对资源来设置的。
java
package sentinel;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 配置规则.
initFlowRules();
for (int i = 0; i < 30; i++) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world" + i);
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!" + i);
}
}
}
}
- 定义规则
接下来,通过流控规则来指定允许该资源通过的请求次数,例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。
java
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}

四、spring cloud 集成 sentinel
我们使用【JavaEE】【SpringCloud】网关_GateWay 这篇文章的项目接着开发。
- 添加依赖:
order-service添加依赖:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 配置sentinel控制台
order-service添加配置:
java
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 #sentinel控制台地址
client-ip: 127.0.0.1 #连接本地Sentinel 可以不配置, 连接服务器Sentinel 需要配置⼀下client-ip
现在调用order-service的任意接口,都会出发sentinel的监控。

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

报错
5.2 基于QPS/并发线程数的流量控制
QPS:每秒请求数,QPS的阈值就是每秒允许请求的最大次数。
并发线程数:进行线程隔离,线程数限流⽤于保护业务线程数不被耗尽。
⽐如A调⽤B,⽽B服务因为某种原因导致服务不稳定或者响应延迟,那么对于A服务来说,它的吞吐量会下降,也意味着占⽤更多的线程(线程阻塞之后⼀直末释放),极端情况下会造成线程池耗尽。针对这种问题,业内有使⽤隔离的⽅案,⽐如通过不同业务逻辑使⽤不同线程池来隔离业务⾃⾝之间的资源争抢(线程池隔离,或者使⽤信号量来控制同时请求的个数(信号量隔离)。这种隔离⽅案虽然能够控制线程数量,但⽆法控制请求排队时间,当请求过多时排队也是⽆益的,直接拒绝能够迅速降低系统压⼒。如果超出阈值,新的请求会被⽴即拒绝。
5.3 流控效果
分别对应 FlowRule 类中的 controlBehavior 字段。
取值分别为:
- 快速失败:
RuleConstant.CONTROL_BEHAVIOR_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才会一直是设置的阈值。
请求阈值初始值是 maxThreshold / coldFactor,持续指定时⻓后,逐渐提⾼到maxThreshold值,coldFactor的默认值是3。
阈值⼀般是⼀个微服务能承受的最⼤QPS,但是⼀个服务刚刚启动时,⼀切资源尚未初始化,如果直接将QPS跑到最⼤值,可能导致服务瞬间宕机。
该⽅式主要⽤于系统⻓期处于低⽔位的情况下,当流量突然增加时,直接把系统拉升到⾼⽔位可能瞬间把系统压垮的,通过"冷启动",让通过的流量缓慢增加,在⼀定时间内逐渐增加到阈值上限,给冷系统⼀个预热的时间,避免冷系统被压垮的情况。
Jmeter设置10秒发100条请求,看sentinel实时监控,通过请求逐渐增加。
5.3.3 排队等待
排队等待,对应RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER这种⽅式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过。可以理解为让所有请求进⼊⼀个队列中,然后按照阈值允许的时间间隔依次执⾏。后⾯的请求必须等待前⾯执⾏完成,直到超时。
这种⽅式主要⽤于处理间隔性突发的流量,例如消息队列。
在某⼀秒有⼤量的请求到来,⽽接下来的⼏秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,⽽不是在第⼀秒直接拒绝多余的请求
超时时间也就是请求等待时⻓。
⽐如阈值为1,那么每秒执⾏⼀个请求。超时时间为5s。现在⼀下⼦来了10个请求,那么:
第3个请求的 等待时⻓为:100 * (3-1) = 2000ms
第6个请求的 等待时⻓为:100 * (6-1) = 5000ms
也就是说,同⼀时间发起10个请求,那么会通过6个,拒绝4个。
使用jmeter一秒钟发送10个请求:
5.4 流控模式

调⽤关系包括调⽤⽅,被调⽤⽅。⽅法⼜可能会调⽤其它⽅法,形成⼀个调⽤链路的层次关系。Sentinel记录资源之间的调⽤链路,这些资源通过调⽤关系,相互之间构成⼀棵调⽤树。

Sentinel根据这些调⽤关系,建⽴不同资源间的调⽤关系,并记录每个资源的实时统计信息。有了调⽤链路的统计信息,我们可以衍⽣出多种流量控制⼿段。
5.4.1 直接
这是默认的流量控制⽅式。当QPS超过任意规则的阈值后,对当前资源直接限流。
针对来源:
default :表⽰不区分调⽤者,来⾃任何调⽤者的请求都将进⾏限流统计。如果这个资源名的调⽤总和超过了这条规则定义的阈值,则触发限流。
5.4.2 链路
链路限流是指:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流。
假如有这两条链路:

在order-service中添加链路:
OrderController加:
java
@RequestMapping("/write")
public String write(){
orderService.queryOrderInfo();
return "写操作";
}
@RequestMapping("/read")
public String read(){
orderService.queryOrderInfo();
return "读操作";
}
OrderService加上:
java
@SentinelResource("queryOrderInfo")
public void queryOrderInfo(){
System.out.println("查询订单详情");
}
但是这样链路显示还是不完全的:
链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进⼊SpringMVC的所有请求设置同⼀个root资源,会导致链路模式失效。我们需要关闭这种对SpringMVC的资源聚合,修改配置如下:
yml
spring:
cloud:
sentinel:
web-context-unify: false #关闭context整合
链路显示完全了:
针对⼊⼝资源为 /order/write 的请求进⾏限流
jmeter创建线程组,添加两个HTTP取样器,分别对应接⼝为 /order/wirte 和 /order/read
结果:发现write接⼝被限流了,read接⼝未限流
5.4.3 关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。
关联限流就是,统计与当前资源相关的另⼀个资源,触发阈值时,对当前资源限流。
⽐如对数据库同⼀个字段的读操作和写操作存在争抢,读的速度过⾼会影响写的速度,写的速度过⾼会影响读的速度。如果放任读写操作争抢资源,则争抢本⾝带来的开销会降低整体的吞吐量。可使⽤关联限流来避免具有关联关系的资源之间过度的争抢。
设置流控规则:
当 /order/wirte 资源访问量触发阈值时,就会对 /order/read 资源限流,避免影响 /order/wirte 资源
应⽤场景:
- 在数据库操作中,读操作和写操作可能会争抢资源(如锁)。通过关联模式,可以限制读操作的频率,以避免对写操作造成过⼤的影响
- 订单服务可能依赖于库存服务,如果库存服务的请求量过⼤,可以通过关联模式限制订单服务的请求量
创建测试任务
创建线程组,添加两个HTTP取样器,分别对应接⼝为 /order/wirte 和 /order/read
结果:写操作没有被限流,读操作被限流了。
六、热点参数限流
热点:经常访问的数据。
在上⾯的流量控制配置中,是对⼀个接⼝进⾏限流,所有的请求参数⼀视同仁,只要达到阈值,就⼀起限流。
热点参数限流会统计传⼊参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调⽤进⾏限流。热点参数限流可以看做是⼀种特殊的流量控制,仅对包含热点参数的资源调⽤⽣效。
6.1 热点参数限流配置
定义资源
热点参数限流对默认的Spring MVC 资源⽆效,在资源上需要加 @SentinelResource:

设置热点参数限流
在需要进⾏热点参数限流的资源后⾯,点击 [+热点]
对 /sentinel/id 这个资源的0号参数(第⼀个参数)做统计,每1秒相同参数值的请求数不能超过5
参数索引:资源热点参数的索引,从0开始。
阈值达到5后,就被限流了:
6.2 参数例外项
热点参数配置中,所有的参数⼀视同仁,QPS都被限定为阈值,但是在⼀些场景下,我们希望可以对某些热点参数进⾏单独限流。

配置说明:
单机阈值:对第⼀个参数的值进⾏统计,当相同值参数阈值超过5时,进⾏限流
参数例外项:针对⼀些特殊的参数,进⾏单独设置
参数类型:热点参数的类型,只⽀持 int,double,String,long,float,char,byte
参数值:特殊的参数值
限流阈值:对这个参数进⾏额外的阈值设置
例如⼀些热点商品,⽐如排名⽐较靠前的,访问次数⽐较多,我们希望可以对这个热点商品进⾏单独限流,让他的阈值⾼⼀些或者低⼀些,就需要使⽤到热点参数限流规则⾥⾯的⾼级选项了。

配置:参数1的QPS限流阈值为8,参数2的限流阈值为15,其他的参数为5。
结果:
七、限流算法
常见四种限流算法:
- 计数器算法/固定窗口限流算法
- 滑动窗口算法
- 漏桶算法
- 令牌桶算法
7.1 计数器算法
在一个指定的而时间周期内,使用一个计数器来记录访问次数,当达到阈值时,这个周期就触发限流策略。进入下一个周期,计数器重置为0。
如图,限定了⼀分钟能够处理的请求数为100,在第⼀个⼀分钟内,共请求了60次。第⼆个⼀分钟,counter⼜从0开始计数,在⼀分半钟时,已经达到了最⼤限流的阈值,这个时候后续的所有请求都会被拒绝。
计数器算法:当请求集中在两个时间周期的交界处,可能会导致请求量过大超出阈值。
如图所⽰,在第⼀分钟的0:58和第⼆分钟的1:02这个时间段内,分别发出了100个请求,整体来看就会出现4秒内总的请求量达到200,超出了设置的每分钟100的阈值。
7.2 滑动窗⼝算法
为了解决计数器算法带来的临界问题,所以引⼊了滑动窗⼝算法。滑动窗⼝是⼀种流量控制技术,在TCP⽹络通信协议中,就采⽤了滑动窗⼝算法来解决⽹络拥塞的情况。这也是sentinel采用的限流算法。
我们每次向后移动窗口的时候,移动大小其实就相当于一个小的窗口。整体上看就是将一个大的窗口划分成数个小窗口,分别在每个⼩时间窗⼝中记录访问次数,然后根据时间将窗⼝往前滑动并删除过期的⼩时间窗⼝。最终只需要统计滑动窗⼝范围内的所有⼩时间窗⼝总的计数即可。
如图所⽰,我们将⼀分钟拆分为4个⼩时间窗⼝,每个⼩时间窗⼝最多能够处理25个请求。并且通过虚线框表⽰滑动窗⼝的⼤⼩(当前窗⼝的⼤⼩是2,也就是在这个窗⼝内最多能够处理50个请求)。同时滑动窗⼝会随着时间往前移动,⽐如前⾯15s结束之后,窗⼝会滑动到15s~45s这个范围,然后在新的窗⼝中重新统计数据。这种⽅式很好地解决了固定窗⼝算法的临界值问题。
7.3 漏桶算法
漏桶限流算法的主要作⽤是控制数据注⼊⽹络的速度,平滑⽹络上的突发流量。
它的原理很简单,可以认为就是注⽔漏⽔的过程,往漏桶中以任意速率流⼊⽔,以固定的速率流出⽔。当⽔超过桶的容量时,会被溢出,也就是被丢弃。因为桶容量是不变的,保证了整体的速率。
就是桶的大小固定,出桶的速度限制,进桶的速度不限但是满了就丢弃进不来。
如图所⽰:在漏桶算法内部同样维护⼀个容器,这个容器会以恒定速度出⽔,不管上⾯的⽔流速度多快,漏桶⽔滴的流出速度始终保持不变。实际上消息中间件就使⽤了漏桶限流的思想,不管⽣产者的请求量有多⼤,消息的处理能⼒取决于消费者。
漏桶算法会有以下缺点:
- ⽆法处理突发流量:漏桶算法以固定的速率处理请求,当流量突然增加时,⽆法快速响应和处理这些请求。超出漏桶容量的请求会被丢弃,这可能导致⽤⼾体验下降。
- 可能导致请求延迟:由于漏桶的流出速率是固定的,即使在流量较⼩的情况下,请求也需要排队等待处理,这可能导致请求的响应时间变⻓。
7.4 令牌桶算法
令牌桶是⽹络流量整形和速率限制中最常使⽤的⼀种算法。对于每⼀个请求,都需要从令牌桶中获得⼀个令牌,如果没有获得令牌,则触发限流策略。
如图:系统会以⼀个恒定速度往固定容量的令牌桶中放⼊令牌,如果此时有客⼾端请求过来,则需要先从令牌桶中拿到令牌以获得访问资格。
假设令牌⽣成速度是每秒10个,也就等同于QPS=10,在请求获取令牌的时候,会存在三种情况:
- 请求速度 > 令牌⽣成速度:令牌会很快被取完,后续再进来的请求会被限流。
- 请求速度 = 令牌⽣成速度:流量处于平稳状态。
- 请求速度 < 令牌⽣成速度:说明系统的并发数不⾼,请求能被正常处理。
由于令牌桶有固定的⼤⼩,当请求速度⼩于令牌⽣成速度时,令牌桶会被填满。所以令牌桶能够处理突发流量。也就是在短时间内新增的流量系统能够正常处理,这是令牌桶的特性。
八、熔断降级
⼀个服务常常会调⽤别的模块,可能是另⼀个远程服务,数据库,或者第三⽅ API 等。
现代微服务架构都是分布式的,由⾮常多的服务组成。不同服务之间相互调⽤,组成复杂的调⽤链路。复杂链路上的某⼀环不稳定,就可能会层层级联,最终导致整个链路都不可⽤。因此我们需要对不稳定的弱依赖(非核心依赖)服务调⽤进⾏熔断,暂时切断不稳定调⽤,避免局部不稳定因素导致整体的雪崩。
熔断降级作为保护⾃⾝的⼿段,通常在客⼾端(调⽤端)进⾏配置。Sentinel 提供了三种熔断策略:慢调⽤,异常⽐例,异常数。
- 慢调⽤⽐例 ( SLOW_REQUEST_RATIO ):需要设置允许的慢调⽤ RT(即最⼤的响应时间),请求的响应时间⼤于该值则统计为慢调⽤。在指定时间内,如果请求数量 > 设定的最⼩数量,且慢调⽤⽐例 > 设定的阈值,则触发熔断
- 异常⽐例 ( ERROR_RATIO ):指定时间内,请求数量 > 设置的最⼩数量,且异常⽐例⼤于阈值,则触发熔断
- 异常数 ( ERROR_COUNT ):指定时间内,异常数⽬超过阈值后,则触发熔断。
8.1 状态机
熔断的思路是由断路器 (或者叫熔断器) 统计服务调⽤的慢请求⽐例,异常⽐例等,如果超过阈值,则熔断该服务,也就是拦截对该服务的请求,当服务恢复时,断路器会放⾏访问该服务的请求。
断路器控制熔断和放⾏是通过状态机来完成的

状态机有三个状态:
- Closed:关闭状态,所有请求都会通过断路器,并开始统计慢请求⽐例,异常⽐例,超过阈值则切换到 open状态。
- Open:打开状态,服务调⽤被熔断。这时所有访问被熔断服务的请求都会被拒绝。
- Half-open:半开状态,当经过⼀段时间后,断路器会从Open状态切换到Half-open状态,这时会有⼀定数量的请求被放⼊,根据这些请求的失败率来判断后续操作。
-
- 失败率低于阈值:切换到closed状态
-
- 失败率超过阈值:切换到open状态
8.2 慢调用比例
慢调⽤⽐例:需要设置允许的慢调⽤RT,也就是最⼤响应时间。
当统计时⻓内请求数⽬ > 设置的最⼩请求数⽬,并且慢调⽤的⽐例⼤于阈值,则接下来的熔断时⻓内请求会⾃动被熔断。经过熔断时⻓后熔断器会进⼊探测恢复状态(HALF-OPEN 状态),若接下来的⼀个请求响应时间⼩于设置的慢调⽤ RT 则结束熔断,若⼤于设置的慢调⽤ RT 则会再次被熔断。
8.2.1 模拟慢调⽤
模拟product-service慢响应
java
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) throws InterruptedException {
//模拟慢响应
long millis = new Random().nextInt(20)+50;
Thread.sleep(millis);
log.info("接收参数, productId:"+productId + ", sleep:"+millis);
return productService.selectProductById(productId);
}
8.2.2 定义资源
在order-service中定义资源,⽅便做熔断处理 @SentinelResource("selectOrderById")

8.2.3 配置熔断规则
超过50ms的调⽤是慢调⽤,统计最近10000内的请求,如果请求量超过5次,并且慢调⽤⽐例不低于0.5,则触发熔断。熔断时⻓为5s,然后进⼊half-open状态,放⾏⼀次请求做测试。

8.2.4 验证熔断结果
后端日志:

8.3 降级
当调⽤失败后,业务直接报错,给⽤⼾体验不太好,应该返回⽤⼾⼀个友好提⽰或者默认结果,这个就是降级。
通常有以下⽅式:
- 捕获异常,根据异常进⾏降级逻辑处理
- 通过FallbackFactory,对远程调⽤的异常做处理。
第⼀种⽅式更通⽤,适合各种场景,第⼆种⽅式需要远程调⽤的服务进⾏处理。这两种⽅式在服务开发中经常搭配着使⽤。
8.3.1 捕获异常
对资源代码进⾏异常捕获,当发⽣熔断时,返回空对象。
OrderController中捕获异常。
java
@SentinelResource("/sentinel/id")
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
try {
return orderService.selectOrderById(orderId);
}catch (UndeclaredThrowableException e){
log.error("获取订单详情发⽣异常, e:", e);
return new OrderInfo();
}
}

8.3.2 FallbackFactory
FallbackFactory 是⼀个在微服务架构中⽤于实现服务降级的接⼝,当远程服务调⽤失败或超时,FallbackFactory 会根据提供的异常信息创建⼀个降级处理实例,以替代原服务调⽤。并且可以根据错误类型,返回不同的降级响应。
在product-api 定义降级处理类:
java
package com.cloud.fallback;
import com.cloud.api.ProductApi;
import com.cloud.model.ProductInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@Slf4j
public class ProductFallbackFactory implements FallbackFactory<ProductApi> {
@Override
public ProductApi create(Throwable cause) {
return new ProductApi() {
@Override
public ProductInfo getProductById(Integer productId) {
log.error("查询信息异常");
return new ProductInfo();
}
@Override
public String p1(Integer id) {
return "发⽣错误";
}
@Override
public String p2(Integer id, String name) {
return "发⽣错误";
}
@Override
public String p3(ProductInfo productInfo) {
return "发⽣错误";
}
@Override
public String p4(ProductInfo productInfo) {
return "发⽣错误";
}
};
}
}
product-api上配置fallbackFactory:
java
@FeignClient(value = "product-service", path = "/product", fallbackFactory = ProductFallbackFactory.class)
添加调用配置类:
java
package com.cloud.product.config;
import com.cloud.fallback.ProductFallbackFactory;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfiguration {
@Bean
public ProductFallbackFactory productFallbackFactory(){
return new ProductFallbackFactory();
}
}
在调⽤⽅order-service上设置配置⽂件:
java
@EnableFeignClients(clients = {ProductApi.class}, defaultConfiguration = DefaultFeignConfiguration.class)
在order-service修改配置,开启 feign 对 Sentinel 功能
yml
feign:
sentinel:
enabled: true # 开启feign对sentinel的⽀持

验证熔断结果:

8.4 异常⽐例
异常⽐例 ( ERROR_RATIO ):统计单位时⻓( statIntervalMs )内,请求数⽬ ⼤于 最⼩请求数⽬,并且异常的⽐例⼤于阈值,则触发熔断。经过熔断时⻓后熔断器会进⼊探测恢复状态(HALF-OPEN 状态),若接下来的⼀个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
8.4.1 模拟异常
com.cloud.product.controller类中修改:
java
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer
productId) throws InterruptedException {
if (productId==1001){
//模拟慢响应
Thread.sleep(60);
}else if(productId==1002){
// 模拟异常
throw new RuntimeException("发⽣异常");
}
return productService.selectProductById(productId);
}
8.4.2 配置熔断规则
统计最近10000ms内的请求,如果请求量超过5次,并且异常⽐例不低于0.5,则触发熔断。

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

调用结果与异常比例相同。
九、授权规则
微服务架构中,许多系统包含敏感信息,如个⼈⾝份信息,财务数据,商业机密等。只有经过验证或者授权的⽤⼾才可以访问这些数据。⽐如在医疗系统中,只有授权的医⽣和护⼠可以查看患者的病历。
授权规则是对请求者的⾝份进⾏判断,决定是否允许该请求访问特定资源。
Sentinel提供了两种授权模式:⽩名单和⿊名单
- ⽩名单:请求来源位于⽩名单内的调⽤者才允许访问。
- ⿊名单:请求来源位于⿊名单内的调⽤者不允许访问,其余请求通过。
Sentinel根据来源来进⾏判断,所以调⽤⽅需要设置来源,被调⽤⽅需要获取来源
Sentinel进⾏授权管理,主要分以下⼏步:
- 服务端(被调⽤⽅)获取来源
- 客⼾端(调⽤⽅)设置来源
- 配置授权规则,即⿊⽩名单
9.1 服务端(被调⽤⽅)获取来源
服务端也就是order-service项⽬。
Sentinel是通过 RequestOriginParser 这个接⼝的 parseOrigin 来获取请求的来源的。
在服务端中实现RequestOriginParser这个接⼝即可
定义请求来源放在Header中,key为origin(⾃定义)
java
package com.cloud.order.config;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 1.获取请求头中的origin
String origin = request.getHeader("origin");
//非空判断
if (!StringUtils.hasText(origin)){
origin = "default";
}
return origin;
}
}
9.2 客⼾端(调⽤⽅)设置来源
也就是gateway项⽬
可以通过AddRequestHeader Filter设置Header
pom文件中加上filter
java
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway
9.3 配置授权规则
对order-service的资源配置授权规则
流控应⽤:即配置对应的⽩/⿊名单,多个应⽤使⽤英⽂逗号分割。
以下配置表⽰,来源为gateway的放⾏通过。

网关访问通过:

十、定义异常返回结果
Sentinel提供了⼀个接⼝ BlockExceptionHandler ,⽤于⾃定义处理 BlockException 异常。
当请求被 Sentinel 限流、降级或授权拒绝时,会抛出 BlockException 。通过实现 BlockExceptionHandler 接⼝,可以定义统⼀的异常处理逻辑,返回更友好的错误信息或执⾏特定的降级操作。
默认的处理源码:

⾃定义处理,异常处理
我们如果需要修改这个提⽰,为⽤⼾提供更好的体验,通常需要⾃定义 BlockExceptionHandler 。
java
package com.cloud.order.handel;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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 jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
@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");
String msg = "Blocked by Sentinel (flow limiting)";
int status = 429;
PrintWriter out = response.getWriter();
//限流异常
if (e instanceof FlowException) {
msg = "触发限流, 请联系服务⽅进⾏调整";
} else if (e instanceof DegradeException) { //降级异常
msg = "触发降级异常";
} else if (e instanceof AuthorityException) { //授权异常
msg = "没有权限访问, 请联系服务⽅进⾏调整";
status = 401;
}
response.setStatus(status);
out.print(msg);
out.flush();
out.close();
}
}

使⽤ @SentinelResource 注解
除了全局的 BlockExceptionHandler ,还可以在⽅法上使⽤ @SentinelResource 注解来指定特定的异常处理⽅法。
上⾯异常的处理,相对⽐较统⼀,如果我们希望给⼀些资源进⾏特殊的定义,就可以使⽤@SentinelResource 的 blockHandler/blockHandlerClass 属性。
官方文档:https://sentinelguard.io/zh-cn/docs/annotation-support.html
在order-service的controller中修改:
java
@SentinelResource(value = "/sentinel/id", blockHandler = "handleException")
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
return orderService.selectOrderById(orderId);
}
public OrderInfo handleException(Integer orderId, BlockException e) {
System.out.println("进⼊异常处理⽅法");
if (e instanceof FlowException) {
return new OrderInfo();
}
return new OrderInfo();
}
blockHandler /blockHandlerClass:blockHandler 对应处理 BlockException的函数名称。
blockHandler 函数访问范围需要是 public,返回类型需要与原⽅法相匹配,参数类型需要和原⽅法相匹配并且最后加⼀个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原⽅法在同⼀个类中。若希望使⽤其他类的函数,则可以指定 blockHandlerclass 为对应的类的 class 对象,注意对应的函数必须为 static 函数,否则⽆法解析。
使⽤中注意事项:
- 处理⽅法必须与受保护资源在同⼀个类中
- 处理⽅法的修饰符必须为 public
- 处理⽅法的参数列表必须和受保护资源相同,并在最后加上 BlockException e 接收异常
- 处理⽅法的返回值必须和受保护资源相同
- 如果使⽤ blockHandlerClass 属性,处理⽅法必须是 public static 的
- 限流规则⾥的资源名必须和@SentinelResource注解声明的资源⼀致
十一、管理规则与推送
前⾯的配置中,服务重启后,我们之前设定的规则就会消失。这是因为 Sentinel 默认是将这些管理规则保存在内存中,在⽣产环境中,这个问题是不可接受的。规则的丢失会导致系统失去流量控制和保护机制。
⽣产环境中,Sentinel需要规则持久化,sentinel-core 提供 API 和扩展接⼝来接收信息。开发者需要根据⾃⼰的环境,选取⼀个可靠的推送规则⽅式。同时,规则最好在控制台中集中管理。
关于规则持久化,Sentinel也给出了⼀些建议:https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel
一般来说,规则的推送有下面三种模式:
| 推送模式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
| Pull 模式 | 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
| Push 模式 | 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性; | 快速 引入第三方依赖 |
11.1 原始模式
如果不做任何修改,Dashboard 的推送规则⽅式是通过 API 将规则推送⾄客⼾端并直接更新到内存中。

11.2 Pull模式
pull 模式的数据源(如本地⽂件、RDBMS 等)⼀般是可写⼊的。
使⽤时需要在客⼾端注册数据源:将对应的读数据源注册⾄对应的 RuleManager,将写数据源注册⾄ transport 的 WritableDataSourceRegistry 中
java
package com.cloud.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 ruleDir = "d:/Users/ys-lenovo/Desktop/sentinel/rules/orderService";
String flowRulePath = ruleDir + "/flow-rule.json";
//创建目录和文件
mkdirIfNotExist(ruleDir);
createFileIfNotExist(flowRulePath);
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 <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
private void mkdirIfNotExist(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExist(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
}
在resources下创建META-INF/services⽬录,并创建⽂件:com.alibaba.csp.sentinel.init.InitFunc,写本地数据源的路径。

重启服务,添加流控规则,可以看见本地已经生成文件了:

再次重启服务,规则依然保存(需要请求⼀下,规则才可以看到),修改配置⽂件,控制台也会跟着更新。
11.3 Push模式
⽣产环境下⼀般更常⽤的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中⼼
(ZooKeeper,Nacos,Apollo等等),推送的操作不应由 Sentinel 客⼾端进⾏,⽽应该经控制台统⼀进⾏管理,直接进⾏推送,数据源仅负责获取配置中⼼推送的配置并更新到本地。因此推送规则正确做法应该是 配置中⼼控制台/Sentinel 控制台 → 配置中⼼ → Sentinel 数据源 → Sentinel,⽽不是经Sentinel数据源推送⾄配置中⼼。这样的流程就⾮常清晰了:

官方文档:https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html
11.3.1 11.3.1 Nacos官⽅⽀持
借助 Sentinel 的 InitFunc SPI 扩展接⼝.只需要实现⾃⼰的 InitFunc 接⼝,在 init ⽅法中编写注册数据源的逻辑。
在order- service 中添加依赖:
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
注册数据源:
java
package com.cloud.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 {
// nacos server ip
private static final String remoteAddress = "ip:端口号";
// nacos group
private static final String groupId = "SENTINEL_GROUP";
// nacos dataId
private static final String dataId = "order-service-flow-rules";
@Override
public void init() throws Exception {
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new
NacosDataSource<>(remoteAddress, groupId, dataId,
source -> JSON.parseObject(source, new
TypeReference<List<FlowRule>>() {}
));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
}
配置数据源路径
在resources下创建META-INF/services⽬录,并创建⽂件:com.alibaba.csp.sentinel.init.InitFunc,写本地数据源的路径

配置Nacos

重启服务,会发现Sentinel Dashboard已经从nacos获取规则配置信息了
- 修改Nacos⽂件,发现Sentinel Dashboard 会及时更新
- 通过Sentinel Dashboard修改规则,发现Nacos配置⽂件并没有同步更新
11.4 Sentinel集成Nacos持久化
上述我们发现,Nacos修改的内容会及时同步到Sentinel DashBoard,但是Sentinel DashBoard更新的内容,并不会同步到Nacos,这在⽣产环境中,也是不⽅便使⽤的。所以我们需要修改Sentinel的源码,让其⽀持双向通讯。
11.4.1 源码改造
-
下载Sentinel源码
-
修改pom⽂件
解压,使⽤idea打开,修改pom⽂件
在sentinel-dashboard源码的pom⽂件中,nacos的依赖默认的scope是test,只能在测试时使⽤。
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
注掉 test
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- 添加Nacos⽀持
sentinel-dashboard 的 test 包下,已经编写了对nacos的⽀持
把 com.alibaba.csp.sentinel.dashboard.rule.nacos ⽂件复制到src/main/java/com.alibaba.csp.sentinel.dashboard.rule ⽬录下

-
修改Nacos配置
修改NacosConfig
从配置中读取,或者直接写死地址

-
配置Nacos数据源
开启 Controller 层操作 Nacos 的开关
代码位于:
com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2

-
修改前端⻚⾯
前端共3处修改
修改src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
将这部分注释去掉:

修改 src/main/webapp/resources/app/scripts/controllers/identity.js,修改 FlowServiceV1 -> FlowServiceV2

修改 /dashboard/flow/ -> /dashboard/v2/flow/

- 重新打包,启动
只需要打包

11.4.2 配置Nacos
修改order-service 服务,让其监听Nacos的规则配置
- 引入依赖(前面已经引过了)
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- 配置Nacos地址
添加:
java
feign:
datasource:
flow-rules: #流控规则
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
rule-type: flow
重启服务,通过Sentinel Dashboard设置流控规则,观察Nacos变化。






























