赞
踩
在《Sentinel 极简入门》中,我们简单了解了 Sentinel,并搭建了 Sentinel 控制台。如果还没看的胖友,可以先看看该文的「1. 概述」和「2. 控制台」小节。
Sentinel 功能比较强大,同时胖友可能对服务容错可能比较陌生,所以我们跟着示例,一个一个来学习噢。
示例代码对应仓库:lab-46-sentinel-demo。
在本小节,我们来学习下 Sentinel 的流量控制功能,对应《Sentinel 官方文档 —— 流量控制》文章。
流量控制,在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
设计理念
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
下面,我们来搭建一个流量控制的 Spring Boot 示例。
在 pom.xml
文件中,引入相关依赖。
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.2.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <modelVersion>4.0.0</modelVersion>
-
- <artifactId>lab-46-sentinel-demo</artifactId>
-
- <dependencies>
- <!-- 实现对 SpringMVC 的自动化配置 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
-
- <!-- Sentinel 核心库 -->
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-core</artifactId>
- <version>1.7.1</version>
- </dependency>
- <!-- Sentinel 接入控制台 -->
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-transport-simple-http</artifactId>
- <version>1.7.1</version>
- </dependency>
- <!-- Sentinel 对 SpringMVC 的支持 -->
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-spring-webmvc-adapter</artifactId>
- <version>1.7.1</version>
- </dependency>
- </dependencies>
-
- </project>

在 cn.iocoder.springboot.lab46.sentineldemo.config
包下,创建 SpringMvcConfiguration 配置类,自定义 sentinel-spring-webmvc-adapter
提供的拦截器。代码如下:
- @Configuration
- public class SpringMvcConfiguration implements WebMvcConfigurer {
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // Add Sentinel interceptor
- // addSentinelWebTotalInterceptor(registry);
- addSentinelWebInterceptor(registry);
- }
-
- private void addSentinelWebInterceptor(InterceptorRegistry registry) {
- // <1.1> 创建 SentinelWebMvcConfig 对象
- SentinelWebMvcConfig config = new SentinelWebMvcConfig();
- config.setHttpMethodSpecify(true); // <1.2> 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。
- // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // <1.3> 设置 BlockException 处理器。
-
- // <2> 添加 SentinelWebInterceptor 拦截器
- registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
- }
-
- private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {
- // <1> 创建 SentinelWebMvcTotalConfig 对象
- SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
-
- // <2> 添加 SentinelWebTotalInterceptor 拦截器
- registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
- }
-
- }

#addSentinelWebTotalInterceptor(InterceptorRegistry registry)
方法,添加 SentinelWebTotalInterceptor 拦截器。#addSentinelWebInterceptor(InterceptorRegistry registry)
方法,添加 SentinelWebInterceptor 拦截器。SentinelWebInterceptor 拦截器,针对每个 URL 进行流量控制。
<1.1>
处,创建 SentinelWebMvcConfig 对象,用于作为 SentinelWebInterceptor 拦截器的配置。
<1.2>
处,设置 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。这里有一个非常重要的概念,就是“资源”。
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。 * 对于 SentinelWebInterceptor 拦截器来说,将 URL + Method 作为一个资源,进行流量控制。具体的,可以看看
SentinelWebInterceptor#getResourceName(HttpServletRequest request)
方法的代码。
<1.3>
处,设置 BlockException 的处理器。Sentinel 在流量控制时,当请求到达阀值后,会抛出 BlockException 异常。此时,可以通过定义 BlockExceptionHandler 去处理。这里,我们使用 SpringMVC 提供的全局异常处理机制,具体可见「2.3 GlobalExceptionHandler」。
<2>
处,添加 SentinelWebInterceptor 拦截器到 InterceptorRegistry 中。
SentinelWebTotalInterceptor 拦截器,针对全局 URL 进行流量控制。简单来说,所有 URL 合计流量,全局统一进行控制。
<1>
处,创建 SentinelWebMvcTotalConfig 对象,用于作为 SentinelWebTotalInterceptor 拦截器的配置。<2>
处,添加 SentinelWebTotalInterceptor 拦截器到 InterceptorRegistry 中。在 cn.iocoder.springboot.lab46.sentineldemo.web
包下,创建 GlobalExceptionHandler 配置类,自定义 sentinel-spring-webmvc-adapter
提供的拦截器。代码如下:
- @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller")
- public class GlobalExceptionHandler {
-
- @ResponseBody
- @ExceptionHandler(value = BlockException.class)
- public String blockExceptionHandler(BlockException blockException) {
- return "请求过于频繁";
- }
-
- }
#blockExceptionHandler(...)
方法中,我们处理 BlockException 异常。因为这里是示例,所以处理的比较简单。胖友可以看看《芋道 Spring Boot SpringMVC 入门》的「5. 全局异常处理」小节。在 cn.iocoder.springboot.lab46.sentineldemo.controller
包下,创建 DemoController 类,提供稍后测试流量控制的示例 API。代码如下:
- @RestController
- @RequestMapping("/demo")
- public class DemoController {
-
- @GetMapping("/echo")
- public String echo() {
- return "echo";
- }
-
- @GetMapping("/test")
- public String test() {
- return "test";
- }
-
- }
在 resources
目录下,创建 Sentinel 自定义的sentinel.properties
配置文件。内容如下:
csp.sentinel.dashboard.server=127.0.0.1:7070
csp.sentinel.dashboard.server
配置项,设置 Sentinel 控制台地址。创建 Application.java
类,配置 @SpringBootApplication
注解即可。代码如下:
- @SpringBootApplication
- public class Application {
-
- public static void main(String[] args) {
- // <X> 设置系统属性 project.name,提供给 Sentinel 读取
- System.setProperty("project.name", "demo-application");
-
- // 启动 Spring Boot 应用
- SpringApplication.run(Application.class, args);
- }
-
- }
<X>
处,设置系统属性 project.name,提供给 Sentinel 读取。比较特殊,该配置项无法在 csp.sentinel.dashboard.server
配置文件中设置。启动 Spring Boot 应用。
① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。此时,控制台输出日志如下:
- INFO: log output type is: file
- INFO: log charset is: utf-8
- INFO: log base dir is: /Users/yunai/logs/csp/
- INFO: log name use pid is: false
② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。此时,我们可以看到 demo-application
应用。如下图所示:
③ 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口 10 次。然后点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口的请求情况。如下图所示:
④ 点击 Sentinel 控制台的「簇点链路」菜单,可以看到 GET:/demo/echo
资源。如下图所示:
⑤ 点击 GET:/demo/echo
资源所在列的「流控」按钮,弹出「新增流控规则」。填写流控规则,如下图所示:
GET:/demo/echo
资源被每秒调用一次。更多详细的配置项的说明,胖友后续一定要认真看《Sentinel 官方文档 —— 流量控制》文章,这是 Sentinel 提供的多种规则中最最最常用的一种。⑥ 点击「新增」按钮,完成流控规则的添加。此时,会自动跳转到「流控规则」菜单。如下图所示:
⑦ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"
。
此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:
示例代码对应仓库:lab-46-sentinel-demo。
在本小节,我们来学习下 Sentinel 的流量控制功能,对应《Sentinel 官方文档 —— 熔断降级》文章。
除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。
设计理念
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。
Sentinel 对这个问题采取了两种手段:
1、通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。2、通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
下面,我们来搭建一个熔断降级的 Spring Boot 示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 lab-46-sentinel-demo 项目。
在 DemoController 类中,额外添加 demo/sleep
接口,通过 sleep 100 毫秒,模拟延迟较高的接口。代码如下:
- @GetMapping("/sleep")
- public String sleep() throws InterruptedException {
- Thread.sleep(100L);
- return "sleep";
- }
重新启动 Spring Boot 应用。
友情提示:咱会发现之前配置的流量控制规则不见了,不要慌,后面会详细述说。
① 使用浏览器,访问下 http://127.0.0.1:8080/demo/sleep 接口。因为 Sentinel 客户端是懒加载的。
② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。
然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 GET:/demo/sleep
资源。
之后,点击 GET:/demo/sleep
资源所在列的「降级」按钮,弹出「新增降级规则」。填写降级规则,如下图所示:
GET:/demo/sleep
资源在 5 秒的时间窗口中,如果平均响应时间超过 1 ms,则进行熔断降级。Sentinel 一共有 3 种方式来衡量资源是否稳定:
1、平均响应时间 (
DEGRADE_GRADE_RT
)当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(
count
,以 ms 为单位),那么在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException
)。
注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx
来配置。2、异常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
)当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(
DegradeRule
中的count
)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。3、异常数 (
DEGRADE_GRADE_EXCEPTION_COUNT
)当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若
timeWindow
小于 60s,则结束熔断状态后仍可能再进入熔断状态。
③ 点击「新增」按钮,完成降级规则的添加。此时,会自动跳转到「降级规则」菜单。如下图所示:
④ 使用浏览器,访问 http://127.0.0.1:8080/demo/sleep 接口 6 次,就会有被 Sentinel 服务降级而拒绝,最终返回 "请求过于频繁"
。
此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:
耐心等待几秒,过了这个时间窗口后,继续访问 http://127.0.0.1:8080/demo/sleep 接口,又可以成功返回了。
示例代码对应仓库:lab-46-sentinel-demo。
在本小节,我们来学习下 Sentinel 的热点参数限流功能,对应《Sentinel 官方文档 —— 热点参数限流》文章。
FROM 《Sentinel 官方文档 —— 热点参数限流》
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。
热点参数限流,会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
下面,我们来搭建一个热点参数限流的 Spring Boot 示例。本着省时省力(努力偷懒)的原则,我们继续复用「2. 流量控制」小节的 lab-46-sentinel-demo 项目。
在 pom.xml
文件中,额外引入相关依赖。
- <!-- Sentinel 对【热点参数限流】的支持 -->
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-parameter-flow-control</artifactId>
- <version>1.7.1</version>
- </dependency>
- <!-- Sentinel 对 Spring AOP 的拓展 -->
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-annotation-aspectj</artifactId>
- <version>1.7.1</version>
- </dependency>
sentinel-parameter-flow-control
依赖,实现 Sentinel 对【热点参数限流】的支持。sentinel-annotation-aspectj
依赖,实现 Sentinel 对 Spring AOP 的推展。稍后我们会使用到 Sentinel 提供的 @SentinelResource
注解声明自定义资源,通过 Spring AOP 拦截该注解的方法,从而实现自定义资源的 Sentinel 的处理逻辑。在 DemoController 类中,额外添加 demo/product_info
接口,用于热点参数限流的示例 API。代码如下:
- @GetMapping("/product_info")
- @SentinelResource("demo_product_info_hot")
- public String productInfo(Integer id) {
- return "商品编号:" + id;
- }
@SentinelResource
注解,自定义了 demo_product_info_hot
资源。为什么不直接使用 sentinel-spring-webmvc-adapter
库,自动给该 demo/product_info
接口生成的 GET:/demo/product_info
呢?
sentinel-spring-webmvc-adapter
库提供的 SentinelWebInterceptor 和 SentinelWebTotalInterceptor 拦截器在调用 Sentinel 客户端时,并未传入参数,所以无法进行热点参数限流。@SentinelResource
注解,自定义了 demo_product_info_hot
资源。然后,通过 Spring AOP 拦截该方法的调用,实现 Sentinel 的处理逻辑。在本小节中,就是为了热点参数限流。重新启动 Spring Boot 应用。
① 使用浏览器,访问下 http://127.0.0.1:8080/demo/product_info?id=1 接口。因为 Sentinel 客户端是懒加载的。
② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。
然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 demo_product_info_hot
资源。
之后,点击 demo_product_info_hot
资源所在列的「热点」按钮,弹出「新增热点规则」。填写热点规则,如下图所示:
③ 点击「新增」按钮,完成热点规则的添加。此时,会自动跳转到「热点规则」菜单。如下图所示:
之后,点击 demo_product_info_hot
资源所在列的「编辑」按钮,弹出「编辑热点规则」。填写热点规则,如下图所示:
点击「 保存」按钮,完成编辑。
④ 使用浏览器,访问 http://127.0.0.1:8080/demo/product_info?id=1 接口 2 次,就会有被 Sentinel 热点参数限流而拒绝,最终返回 "请求过于频繁"
。
此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:
此时,我们访问 http://127.0.0.1:8080/demo/product_info?id=2 接口,不会存在限流的情况。而是在快速访问 10 次,才会被限流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。