Sentinel 介绍
这里我直接引用官方的一段话来介绍一下 sentinel。随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
通过官方的描述,我们可以知道这是一个为了保护系统稳定性和微服务质量而设计的组件,用来实现流量控制,防止系统过载,保证微服务之间的稳定交互。这里我主要挑几个重点特性来介绍一下。
流量控制:
对系统的入口流量进行限制,防止系统因为过大的流量压力而崩溃,例如我们可针对某一个接口进行流量设置,1 秒内只允许 50 个请求进入,超过这个阈值,额外的请求会被拒绝掉。
熔断和降级:
如果系统的某个服务出现问题,Sentinel 可以立刻断开这个服务,防止错误进一步扩大。同时,也可以实现服务的降级,在系统压力大的时候,停掉一些非关键服务,让系统的资源集中在优先度更高的任务上。
实时监控:
在 Sentinel 控制台中,我们可以实时看到系统的运行状态,包括请求数量,拦截个数,异常等等。
项目接入
这里我创建了一个 Spring Cloud Alibaba 微服务架构的项目。下面是 cloud 版本和 alibaba 的版本以及 springBoot 的版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2022.0.0</version> <type>pom</type> <scope>import</scope> </dependency>
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2022.0.0.0-RC2</version> <type>pom</type> <scope>import</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.0.2</version> <type>pom</type> <scope>import</scope> </dependency>
|
之后我们需要在需要使用 Sentinel 的服务中单独引入 spring 给我们提供的 starter 包即可。如果需要持久化 Sentinel 的配置规则需要多引入一个 sentinel-datasource-nacos
依赖,使用 nacos
作为数据源
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.8.6</version> </dependency>
|
现在我们需要去下载 Sentinel 的控制台。Sentinel 控制台 GitHub 地址 我当时下载的时候最新的稳定版本是 1.8.6。后面我会以 SpringBoot3.0.2 的版本和 Sentinel 控制台 v1.8.6 来做演示。
控制台下载完成后,我们需要启动起来,使用 java -jar 启动即可,默认端口号是 8080。之后在配置文件中配置一下 Sentinel 的控制台地址。到此,项目就完成了接入
1 2 3 4 5 6 7 8
| spring: cloud: sentinel: transport: dashboard: 127.0.0.1:8818 eager: true
|
浏览器中输入 127.0.0.1:8080 进入 Sentinel 控制台页面。密码和账号都是 sentinel

Sentinel 的使用 - 流量控制
我们先来看一下 Sentinel 的核心功能,流量控制。
@SentinelResource 注解
这个注解是用来定义资源,并且可以对过载的流量进行自定义返回值设定。下面是几个常用的属性
value:
资源的名称,不可以为空
blockHandler:
函数名称,如果本次请求被限流了,会去执行这个函数。该函数的参数必须和资源的参数对应,并且后面需要加一个额外的参数,类型是 BlockException
,同时,函数访问范围必须是 public。函数的位置必须和原方法在一个类里
blockHandlerClass:
这个需要和上一个属性联合使用,上一个属性如果不指定 blockHandlerClass
,那么只能在本类中使用,也可以写在别的类里,然后使用本属性置顶类的位置。同时,对应的函数方法必须被 static 修饰。
控制台流控规则设置
现在我们回到控制台,准备对该接口进行限流,我们需要先访问一次,之后在簇点链路中可以找到对应的资源名称,点击流控,可以看到如下界面。

我们先对这几个属性进行一些简单的介绍,资源名不用多解释,就是我们设置的资源名称,针对来源类似于分组的名字,一般是默认。阈值类型有 2 种。单机阈值就是 1 秒内的请求上限。例如我们选择 QPS 类型,阈值设置 50,就是 1 秒内最多只有 50 个请求可以成功
QPS:每秒可通过的流量次数
线程数:每秒可通过的线程数。可以理解为同时处理的请求数量
点开高级选项,可以看到有个集群选项,这里先不对集群进行介绍,我们直接看流控模式和流控效果。

流控模式
直接:固定的对资源请求数量做限制,也是默认的流控模式
关联:点击关联后,会多一个关联资源的输入框,这里需要输入我们的关联资源名字。例如我们输入 B,阈值设置为 50,当 B1 秒内访问流量超过 50,则对 login
资源进行限流

链路: 同样会让我们输入链路入口资源名称,根据调用链路限流。例如,当通过 B 的调用资源 A 超过 50,对整个调用链路进行限流。
流控效果
快速失败:跟名字一样,将超出的流量直接返回失败作为结果。
Warm Up:预热,先放行一小部分,随着时间推移,逐渐放大限流量。例如,我们设置 1 秒内 1000QPS。 预热时间为 10 秒。那么会在第一秒放行一部分,第二秒一部分,直到第十秒放行到 1000 请求。

下面是官方给出的一个示例图,可以看到流量的放行数量是逐步扩大,并不是 1 秒内直接放行所有的 1000 个请求。相当于是给系统了一个预热时间,如果前面请求的结果存入了缓存,那么后面的请求自然可以更快的返回,避免了瞬时大流量对数据库造成的压力

排队等待:将流量进行排列,和队列一样,按顺序放行到后台。使用此效果的时候会让我们设置一个超时时间,如果我们设置为 2 秒,但 2 秒内该请求并没有访问到后台,那么直接和快速失败一样,返回失败。

代码运行结果
下面是一段演示代码,方法进入后直接返回。
1 2 3 4 5 6 7 8 9 10 11 12
| @SentinelResource(value = "login", blockHandler = "sentinelDefaultMsg", blockHandlerClass = SentinelDefaultMethod.class) @GetMapping("/login/{userId}/{password}") public Result selectUserById(@PathVariable("userId") String userId, @PathVariable("password") String password) {; return Result.success("SUCCESS"); }
public class SentinelDefaultMethod { public static Result sentinelDefaultMsg(String userId, String password, BlockException blockException) { return Result.error("访问过于频繁!"); } }
|
流控规则我设置 1 秒内只允许 1 个流量通过,流控模式使用直接,效果使用快速失败。

之后我不断访问限流的接口,可以在实时监控页面看到系统的流量拦截情况,无论我们访问多少次,1 秒内只有 1 个请求可以顺利通过。

同时,也会返回我们设定好的错误信息。

现在我们尝试一下关联流控模式,增加一个 update 方法,将 update 方法作为关联资源,资源名设置为 updateUser
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @SentinelResource(value = "login", blockHandler = "sentinelDefaultMsg", blockHandlerClass = SentinelDefaultMethod.class) @GetMapping("/login/{userId}/{password}") public Result selectUserById(@PathVariable("userId") String userId, @PathVariable("password") String password) {; System.out.println("我是login接口"); return Result.success("SUCCESS"); }
@SentinelResource(value = "updateUser", blockHandler = "sentinelDefaultMsg", blockHandlerClass = SentinelDefaultMethod.class) @GetMapping("/login/{userId}/{password}") public Result updateUser() { System.out.println("我是updateUser接口"); return Result.success("SUCCESS"); }
|
我们设置流控规则为:如果 updateUser 这个资源在 1 秒内访问流量超过 1 次,那么对 login 这个资源做流控处理。

这里我们对 updateUser 这个资源做压测,在 30 秒内,不断访问 login 这个方法,看看 login 是否被做了流控处理。

开启压测后,我们的 login 方法根本访问不通,证明 Sentinel 的限流规则生效,测试成功

再放一张对照图,我们 update 这个方法在被高并发访问时,login 的请求是全被拒绝的

服务熔断降级
熔断是微服务中的一种保护机制,如果符合我们设定的熔断策略,就会进入熔断这一状态。在指定时间内,所有请求会被 Sentinel 拦截掉。指定时间结束后,进入半开状态,下一次的请求会被判断,如果不符合熔断策略。则取消熔断状态,如果还是符合熔断策略要求,继续进入熔断状态。
慢调用比例
按照程序的响应时间来进行熔断。例如下图设置,首先是比例阈值
,范围是 0-1,这个很好理解。例如我输入 0.2,表示 10 个请求内至少有 2 个有异常则开启熔断。统计时长
则表示统计请求的时间窗口大小。例如设置为 1000ms,则表示在 1s 内,至少有 1 个请求进来(最小请求数
)。此时开启熔断检查,如果有超过 20%(比例阈值
)的请求响应时间超过 300ms(最大RT
),则开启熔断,熔断时长
为 3s

下面是一个测试接口,有 50% 的几率睡眠 500 毫秒,这是符合我们的熔断策略的。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SentinelResource(value = "exception", blockHandler = "sentinelDefaultMsg", blockHandlerClass = SentinelDefaultMethod.class) @GetMapping("/exception") public Result exception() { int nextInt = new Random().nextInt(100) + 1; if (nextInt > 50) { try { Thread.sleep(500L); } catch (InterruptedException e) { throw new RuntimeException(e); } } return Result.success("SUCCESS"); }
|
使用 ApiPost 进行压力测试,通过请求 16 个,失败 15000+,没有问题,因为熔断的 3s 内请求的状态全部为失败。

异常比例
我们修改之前的接口,改为有 50% 的几率抛出异常
1 2 3 4 5 6 7 8 9
| @SentinelResource(value = "exception", blockHandler = "sentinelDefaultMsg", blockHandlerClass = SentinelDefaultMethod.class) @GetMapping("/exception") public Result exception() { int nextInt = new Random().nextInt(100) + 1; if (nextInt > 50) { throw new RuntimeException("异常"); } return Result.success("SUCCESS"); }
|
同时新创建一个熔断规则,规则为 1s 之内有一个请求,且异常比例大于 30%,则开启熔断,9s 钟之内服务不可用。

进行压力测试,结果符合预期,一共就 10s,第一个请求触发熔断,后续的所有请求全部失败

异常数
根据异常数来开启熔断。在统计时长内,达到最小请求数且异常个数也达到则开启熔断。例如下图,表明在 10s 内,有至少 5 个请求且抛出了 5 个异常则开启熔断。因为异常数平时很少使用这里就不写案例了,做一个了解即可。

持久化 Sentinel 配置
首先我们来说一下 Sentinel 的配置结构,配置是以 JSON 格式存储在 nacos 中的。其中又分为限流规则和熔断降级规则两种,我们先来说说限流规则。
resource:资源名
limitApp:表示流控范围,一般使用默认即可
grade:流控维度 0 表示按照并发数量 1 表示 QPS 数量(对应页面阈值类型)
count:表示允许通过的请求数(对应页面单机阈值)
strategy:表示流控模式 0 -> 直接 1-> 关联 2 -> 链路(对应界面流控模式)
controlBehavior:表示流控类型 0 -> 快速失败 1 -> Warm UP 2 -> 排队等待(对应页面流控效果)
clusterMode: 是否启用集群模式
1 2 3 4 5 6 7 8 9 10 11 12
| [ { "resource": "/login", "limitApp": "default", "grade": 1, "count": 5, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]
|
下面是熔断降级的配置:
resource:资源名
limitApp:表示流控范围,一般使用默认即可
grade:熔断降级类型 0 -> 慢调用比例 1 -> 异常比例 2 -> 异常数比例(对应页面熔断策略)
count:表示异常比率(对应页面比例阈值)
timeWindow:表示熔断时间(对应页面熔断时长)
statIntervalMs: 统计时长(对应页面统计时长)
minRequestAmount:统计时间内的最小请求数(对应页面最小请求数)
slowRatioThreshold: 慢调用比例阈值(对应页面比例阈值,此时 count 变为最大 RT。注意,仅在慢调用比例下生效!!!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [ { "resource": "/exception", "limitApp": "default", "grade": 0, "count": 200, "slowRatioThreshold": 0.2, "timeWindow": 10, "minRequestAount": 5 }, { "resource": "/exception", "limitApp": "default", "grade": 1, "count": 0.3, "timeWindow": 10, "minRequestAount": 5 } ]
|
我们使用 nacos 来作为持久化容器。首先我们需要在配置文件中加入 datasource
部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| spring: cloud: sentinel: transport: dashboard: 127.0.0.1:8818 eager: true datasource: flow: nacos: server-addr: 127.0.0.1:8848 namespace: 77654658-9d96-4fdc-a69d-0f0ca7f9ad1a dataId: ${spring.application.name}-${spring.profiles.active}-flow-rules groupId: sentinel_rules rule-type: flow degrade: nacos: server-addr: 127.0.0.1:8848 namespace: 77654658-9d96-4fdc-a69d-0f0ca7f9ad1a dataId: ${spring.application.name}-${spring.profiles.active}-degrade-rules groupId: sentinel_rules rule-type: degrade
|
之后我们每次启动 Sentinel 之后,都会自动将 Nacos 中的配置直接同步到 Sentinel 控制台内。
Sentinel 的实现原理
下图是官网的框架图。在设计上使用了责任链模式。对于规则的统计和处理相互独立,而且可以动态调整规则顺序和增删处理规则。首先外部请求进入后,会先经过 8 个槽(Slot),每个 Slot 会进行一些特定的处理。下面我针对每一个 Slot 来进行一些简单的说明。

NodeSelectorSlot:这个 Slot 主要负责资源的调用路径,生成树状的调用链路信息。并且根据这些调用路径来限流降级,我们可以通过 http://127.0.0.1:8719/tree?type=root
来查看当前的调用链
以下是我的调用链,当作一个参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| EntranceNode: machine-root(t:0 pq:1.0 bq:0.0 tq:1.0 rt:2.0 prq:1.0 1mp:12 1mb:0 1mt:12) -EntranceNode: sentinel_web_servlet_context(t:0 pq:1.0 bq:0.0 tq:1.0 rt:2.0 prq:1.0 1mp:12 1mb:0 1mt:12) --/version(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/cluster/server_state/sora-auth(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/degrade/rules.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/registry/machine(t:0 pq:1.0 bq:0.0 tq:1.0 rt:2.0 prq:1.0 1mp:12 1mb:0 1mt:12) --/paramFlow/rules(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/authority/rules(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/auth/login(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/app/briefinfos.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/system/rules.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/app/sora-gateway/machines.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/metric/queryTopResourceMetric.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/v1/flow/rules(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/app/sora-auth/machines.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/assets/img/sentinel-logo.png(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/v1/flow/rule(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) --/resource/machineResource.json(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0) -EntranceNode: sentinel_default_context(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:0 1mb:0 1mt:0)
t:threadNum pq:passQps bq:blockQps tq:totalQps rt:averageRt prq: passRequestQps 1mp:1m-pass 1mb:1m-block 1mt:1m-total
|
ClusterBuilderSlot:用于构建集群节点以及调用来源节点,维护集群的各项指标
StatisticSlot:这是一个用于统计的 Slot,用来记录资源的运行状况,比如成功的调用数、失败的调用数和响应时间等。并且 Sentinel 底层采用的是高性能滑动窗口数据结构,可以很好的统计实时数据,并且在高并发场景下依旧有很好的表现

FlowSlot:流量控制的 Slot,根据我们设置的规则来判断是否需要对规则进行限流。
DegradeSlot:熔断降级的 Slot,主要在资源的表现不佳(根据资源的平均响应时间以及异常比率),对资源进行熔断降级
SystemSlot:系统保护的 Slot,会在系统负载过高时,对部分资源进行保护,防止系统过载。