《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Alibaba/Sentinel/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslabx-04-spring-cloud-alibaba-sentinel 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

本文我们来学习 Spring Cloud Alibaba 提供的 Spring Cloud Alibaba Sentinel 组件,对 Spring Cloud 进行整合,实现服务容错相关的功能。

FROM https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

在开始本文之前,胖友需要对 Sentinel 进行简单的学习。可以阅读《Sentinel 极简入门》文章,将第一二小节看完,在本机搭建一个 Sentinel 控制台。

友情提示:艿艿本机搭建的 Sentinel 控制台启动在 7070 端口。

2. 流量控制

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

在本小节,我们来学习下 Sentinel 的流量控制功能,对应《Sentinel 官方文档 —— 流量控制》文章。

FROM 《Sentinel 官方文档 —— 主页》

流量控制,在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:流量控制

设计理念

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

下面,我们来搭建一个 Sentinel 流量控制的使用示例。最终示例项目如下图所示:项目结构

2.1 引入依赖

pom.xml 文件中,引入 Spring Cloud Alibaba Sentinel 相关依赖。

<?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>
<artifactId>labx-04</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-04-sca-sentinel-demo01-provider</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>

</project>

通过引入 spring-cloud-starter-alibaba-sentinel 依赖,引入并实现 Sentinel 的自动配置。在该依赖中,已经帮我们自动引入 Sentinel 的大量依赖,非常方便,如下图所示:`spring-cloud-starter-alibaba-sentinel`

2.2 配置文件

创建 application.yaml 配置文件,添加 Sentinel 配置项。配置如下:

spring:
application:
name: demo-provider

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*

Sentinel 配置项,以 spring.cloud.sentinel 开头,对应 SentinelProperties 配置属性类。

enabled 配置项,设置是否开启 Sentinel,默认为 true 开启,所以一般不用主动设置。如果胖友关闭 Sentinel 的功能,例如说在本地开发的时候,可以设置为 false 关闭。

eager 配置项,设置是否饥饿加载,默认为 false 关闭。默认情况下,Sentinel 是延迟初始化,在首次使用到 Sentinel 才进行初始化。通过设置为 true 时,在项目启动时就会将 Sentinel 直接初始化,完成向 Sentinel 控制台进行注册。

transport.dashboard 配置项,设置 Sentinel 控制台地址。

filter.url-patterns 配置项,设置拦截请求的地址,默认为 /*

在 Sentinel 的子项目 sentinel-spring-webmvc-adapter 中,对 SpringMVC 进行适配,通过 SentinelWebInterceptor 拦截器,实现对 SpringMVC 的请求的拦截,使用 Sentinel 进行保护。通过 filter.url-patterns 配置项,可以定义该拦截器的拦截请求地址

不过要注意,因为 filter.url-patterns 配置项的默认值为 /*,只能拦截根目录的请求,显然不满足我们的日常需求,因此艿艿修改成了 /** 拦截所有请求。不了解的胖友,可以阅读下《SpringMVC Ant 路径匹配》文章。

2.3 BlockException 处理器

先来对 BlockException 异常做个简单的了解,在被 Sentinel block 的时候,就会抛出它。BlockException 是一个异常抽象基类,其有 5 个实现类,刚好对应 Sentinel 的 5 种流量控制手段,如下图所示:BlockException 类图

旁白君:暂时找不到 block 适合翻译成什么单词,相对最贴切的可能是阻塞…

在 SentinelWebInterceptor 拦截器中,当请求满足配置的 Sentinel block 的条件时,Sentinel 会抛出 BlockException 异常。通过定义 BlockExceptionHandler 接口的实现类,可以实现对 BlockException 的异常处理。

默认情况下,BlockExceptionHandler 有一个默认的 DefaultBlockExceptionHandler 实现类,返回 Block 字符串提示。代码如下:

public class DefaultBlockExceptionHandler implements BlockExceptionHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// ... 省略其它代码

PrintWriter out = response.getWriter();
out.print("Blocked by Sentinel (flow limiting)");
}

}

显然,在我们使用 SpringMVC 提供 Restful API 时,直接返回字符串提示是不合适的,因为一般是返回 JSON 字符串,例如说:

{
"code": 1024,
"msg": "Blocked by Sentinel (flow limiting)"
}

因此,我们自定义的 CustomBlockExceptionHandler 实现类,直接抛出 BlockException 异常,最终交给自定义的 SpringMVC 全局异常处理器 ,将 BlockException 异常处理成 JSON 字符串提示返回。代码如下:

// CustomBlockExceptionHandler.java
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
throw e;
}

}

// GlobalExceptionHandler.java
@Component
@ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") // 只处理该包下的 Controller 定义的接口
public class GlobalExceptionHandler {

@ResponseBody
@ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。
public JSONObject blockExceptionHandler(BlockException blockException) {
return new JSONObject().fluentPut("code", 1024)
.fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName());
}

}

友情提示:如果胖友对 SpringMVC 的全局异常处理器不了解的话,可以看看《芋道 Spring Boot SpringMVC 入门》文章的「5. 全局异常处理」小节。

2.4 DemoController

创建 DemoController 类,提供稍后测试流量控制的示例 API。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

@GetMapping("/echo")
public String echo() {
return "echo";
}

@GetMapping("/test")
public String test() {
return "test";
}

}

2.5 DemoProviderApplication

创建 DemoProviderApplication 类,作为应用启动类。代码如下:

@SpringBootApplication
public class DemoProviderApplication {

public static void main(String[] args) {
SpringApplication.run(DemoProviderApplication.class, args);
}

}

2.6 简单测试

① 使用 DemoProviderApplication 启动示例应用。在 IDEA 控制台中,可以看到 Sentinel 相关的日志如下:

// ... 省略其它日志

// 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

// 注册 SentinelWebInterceptor 拦截器,拦截路径为 /** 的请求
2020-02-13 23:30:05.574 INFO 49873 --- [ main] c.a.c.s.SentinelWebAutoConfiguration : [Sentinel Starter] register SentinelWebInterceptor with urlPatterns: [/**].

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。此时,我们可以看到 demo-provider 应用。如下图所示:Sentinel 控制台 - 首页

③ 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口 10 次。然后点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口的请求情况。如下图所示:Sentinel 控制台 - 实时监控

④ 点击 Sentinel 控制台的「簇点链路」菜单,可以看到 /demo/echo 资源。如下图所示:Sentinel 控制台 - 簇点链路

⑤ 点击 /demo/echo 资源所在列的「流控」按钮,弹出「新增流控规则」。填写流控规则,如下图所示:Sentinel 控制台 - 新增流控规则

  • 这里,我们创建的是比较简单的规则,仅允许 /demo/echo 资源被每秒调用一次。
  • 更多详细的配置项的说明,胖友后续一定要认真看《Sentinel 官方文档 —— 流量控制》文章,这是 Sentinel 提供的多种规则中最最最常用的一种。

⑥ 点击「新增」按钮,完成流控规则的添加。此时,会自动跳转到「流控规则」菜单。如下图所示:Sentinel 控制台 - 流控规则

⑦ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 FlowException",
"code": 1024
}
  • 流量控制对应 FlowException 异常,因此这里会看到哈。

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Sentinel 控制台 - 实时监控

3. 熔断降级

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

在本小节,我们来学习下 Sentinel 的流量控制功能,对应《Sentinel 官方文档 —— 熔断降级》文章。

FROM 《Sentinel 官方文档 —— 主页》

除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。熔断降级

设计理念

Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。

在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。

Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。

Sentinel 对这个问题采取了两种手段:

1、通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

2、通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

下面,我们来搭建一个 Sentinel 熔断降级制的使用示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目。

3.1 DemoController

DemoController 类中,额外添加 demo/sleep 接口,通过 sleep 100 毫秒,模拟延迟较高的接口。代码如下:

@GetMapping("/sleep")
public String sleep() throws InterruptedException {
Thread.sleep(100L);
return "sleep";
}

3.2 简单测试

友情提示:在测试的过程中,咱会发现之前配置的流量控制规则不见了,不要慌,后面会详细述说。

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:8080/demo/sleep 接口,保证 /demo/sleep 资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 /demo/sleep 资源。

之后,点击 /demo/sleep 资源所在列的「降级」按钮,弹出「新增降级规则」。填写降级规则,如下图所示:Sentinel 控制台 - 新增降级规则

  • 这里,我们创建的是比较简单的规则,当 /demo/sleep 资源在 5 秒的时间窗口中,如果平均响应时间超过 1 ms,则进行熔断降级。

Sentinel 一共有 3 种方式来衡量资源是否稳定:

FROM 《Sentinel 官方文档 —— 流量控制》

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,则结束熔断状态后仍可能再进入熔断状态。

④ 点击「新增」按钮,完成降级规则的添加。此时,会自动跳转到「降级规则」菜单。如下图所示:Sentinel 控制台 - 降级规则

⑤ 使用浏览器,访问 http://127.0.0.1:8080/demo/sleep 接口 6 次,就会有被 Sentinel 服务降级而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 DegradeException",
"code": 1024
}
  • 热点参数限流对应 DegradeException 异常,因此这里会看到哈。

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Sentinel 控制台 - 实时监控

耐心等待几秒,过了这个时间窗口后,继续访问 http://127.0.0.1:8080/demo/sleep 接口,又可以成功返回了。

4. 热点参数限流

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

在本小节,我们来学习下 Sentinel 的热点参数限流功能,对应《Sentinel 官方文档 —— 热点参数限流》文章。

FROM 《Sentinel 官方文档 —— 热点参数限流》

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。

热点参数限流,会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。热点参数限流

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

下面,我们来搭建一个 Sentinel 热点参数限流的使用示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目。

4.1 DemoController

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 接口生成的 /demo/product_info 资源呢?

  • 原因:因为 sentinel-spring-webmvc-adapter 库提供的 SentinelWebInterceptor 拦截器在调用 Sentinel 客户端时,并未传入参数,所以无法进行热点参数限流
  • 解决:使用 Sentinel 提供的 @SentinelResource 注解,自定义了 demo_product_info_hot 资源。然后,通过 Spring AOP 拦截该方法的调用,实现 Sentinel 的处理逻辑。在本小节中,就是为了热点参数限流

友情提示,关于 @SentinelResource 注解,我们在「TODO. 注解支持」小节中,会专门讲解下。

4.2 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:8080/demo/product_info?id=1 接口,保证 /demo/product_info 资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 demo_product_info_hot 资源。

之后,点击 demo_product_info_hot 资源所在列的「热点」按钮,弹出「新增热点规则」。填写热点规则,如下图所示:Sentinel 控制台 - 新增热点规则

  • 这里,我们只设置了参数索引为 0,统计窗口时长为 60 秒,请求最大次数为 10。更多设置,我们继续往下看。

④ 点击「新增」按钮,完成热点规则的添加。此时,会自动跳转到「热点规则」菜单。如下图所示:Sentinel 控制台 - 热点规则

之后,点击 demo_product_info_hot 资源所在列的「编辑」按钮,弹出「编辑热点规则」。填写热点规则,如下图所示:Sentinel 控制台 - 编辑热点规则

  • 这里,我们配置了当第一个参数的值为 1 时,限制在统计窗口中,请求最大次数为 1。

点击「 保存」按钮,完成编辑。

⑤ 使用浏览器,访问 http://127.0.0.1:8080/demo/product_info?id=1 接口 2 次,就会有被 Sentinel 热点参数限流而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 ParamFlowException",
"code": 1024
}
  • 熔断降级对应 ParamFlowException 异常,因此这里会看到哈。

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Sentinel 控制台 - 实时监控

此时,我们访问 http://127.0.0.1:8080/demo/product_info?id=2 接口,不会存在限流的情况。而是在快速访问 10 次,才会被限流。

😈 有一点要特别注意,热点参数限流看起来和「2. 流量控制」基于 QPS 的限流是比较相似的。不过很大的差异是,热点参数限流是针对每个参数,分别计数来限流。举个例子,在当前示例的热点规则下:

详细的,胖友自己可以简单测试下,感受会非常明显哈。

5. 系统自适应限流

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

在本小节,我们来学习下 Sentinel 的系统自适应限流功能,对应《Sentinel 官方文档 —— 系统自适应限流》文章。

FROM 《Sentinel 官方文档 —— 主页》

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

下面,我们来搭建一个 Sentinel 系统自适应限流的使用示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目。

5.1 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「系统规则」菜单,然后点击右上角「新增系统规则」按钮,弹出「新增系统保护规则」。填写降级规则,如下图所示:Sentinel 控制台 - 新增系统保护规则

  • 这里,为了测试方便,我们创建了一条 CPU 超过 1% 后,自动进行系统限流。

Sentinel 一共有 5 种系统规则:

FROM 《Sentinel 官方文档 —— 系统自适应限流》

1、Load 自适应(仅对 Linux/Unix-like 机器生效)

系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5

2、CPU usage(1.5.0+ 版本)

当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

3、平均 RT

当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

4、并发线程数

当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

5、入口 QPS

当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

③ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口,直接就被 Sentinel 系统自适应限流而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 SystemBlockException",
"code": 1024
}

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Sentinel 控制台 - 实时监控

6. 黑白名单控制

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

在本小节,我们来学习下 Sentinel 的黑白名单控制功能,对应《Sentinel 官方文档 —— 黑白名单控制》文章。

FROM 《Sentinel 官方文档 —— 黑白名单控制》

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

下面,我们来搭建一个 Sentinel 黑白名单控制的使用示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目。

6.1 RequestOriginParser

在 Sentinel 的子项目 sentinel-spring-webmvc-adapter 中,定义了 RequestOriginParser 接口,从请求中解析到调用来源,例如说使用 IP、请求头 user、请求头 appName

因为我们要使用 Sentinel 黑白名单控制的功能,所以需要获得请求的调用来。RequestOriginParser 暂时没有提供默认的实现,所以我们自定义 CustomRequestOriginParser 实现类,解析请求头 s-user 作为调用来源。代码如下:

@Component
public class CustomRequestOriginParser implements RequestOriginParser {

@Override
public String parseOrigin(HttpServletRequest request) {
// <X> 从 Header 中,获得请求来源
String origin = request.getHeader("s-user");
// <Y> 如果为空,给一个默认的
if (StringUtils.isEmpty(origin)) {
origin = "default";
}
return origin;
}

}
  • <X> 处,我们从请求头的 "s-user" 对应的值,作为请求来源。注意,Sentinel 黑白名单的控制,一般是服务和服务之间的调用。例如说,配置订单服务允许调用用户服务。
  • <Y> 处,我们判断未获得请求来源的时候,设置默认为 default。原因是,Sentinel 提供的 AuthorityRuleChecker 在进行黑白名单控制时,如果请求来源为空,直接就通过了 =。=

6.2 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口,保证 /demo/echo 资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 /demo/echo 资源。

之后,点击 /demo/echo 资源所在列的「授权」按钮,弹出「新增授权规则」。填写授权规则,如下图所示:Sentinel 控制台 - 新增授权规则

  • 这里,我们配置 /demo/echo 资源,仅仅允许来源为 test 的请求才可以访问。

③ 点击「新增」按钮,完成授权规则的添加。此时,会自动跳转到「授权规则」菜单。如下图所示:Sentinel 控制台 - 授权规则

④ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口时,就会有被 Sentinel 黑白名单控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 AuthorityException",
"code": 1024
}
  • 热点参数限流对应 AuthorityException 异常,因此这里会看到哈。

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Sentinel 控制台 - 实时监控

我们来使用 Postman 来模拟一个来源为 test 的请求,如下图所示:Postman

7. Sentinel 客户端 API

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

为了减少开发的复杂程度,Sentinel 对大部分的主流框架做了适配,例如 SpringMVC、WebFlux、Dubbo、Spring Cloud、RocketMQ 等等。我们只需要引入对应的 sentinel-apache-xxx-adapter 依赖,即可方便地整合 Sentinel。

不过,Sentinel 并不能适配所有框架,此时我们可以使用 Sentinel 客户端 API,手动进行资源的保护。在《Sentinel 官方文档 —— 如何使用》文章的定义资源其它 API 两个小节,详细的介绍了如何使用 Sentinel 客户端 API。

下面,我们来搭建一个 Sentinel 客户端 API 的使用示例。本着省时省力(努力偷懒)的原则,我们直接复用「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目。

7.1 DemoController

DemoController 类中,额外添加 demo/entry_demo 接口,在内部使用 Sentinel 客户端 API 来进行资源的保护。代码如下:

@GetMapping("/entry_demo")
public String entryDemo() {
Entry entry = null;
try {
// <1> 访问资源
entry = SphU.entry("entry_demo");

// <2> ... 执行业务逻辑

return "执行成功";
} catch (BlockException ex) { // <3>
return "被拒绝";
} finally {
// <4> 释放资源
if (entry != null) {
entry.exit();
}
}
}
  • 整个逻辑,和我们使用 Java 进行 I/O 操作的代码比较像,通过 try catch finally 经典套路。
  • <1> 处,调用 Sentinel 的 SphU#entry(String name) 方法,访问资源。其中,参数 name 就是在 Sentinel 中定义的资源名。如果访问资源被拒绝,例如说被限流或降级,则会抛出 BlockException 异常。
  • <2> 处,编写具体的业务逻辑代码。
  • <3> 处,处理访问资源被拒绝所抛出的 BlockException 异常。这里,我们是直接返回 "被拒绝" 的字符串。
  • <4> 处,调用 Sentinel 的 Entry#exit() 方法,释放对资源的访问。注意,entry 和 exit 必须成对出现,不然资源一直被持有者。

这里我们编写的示例是比较简单的,推荐胖友后续自己看下 sentinel-spring-webmvc-adapter 提供的 AbstractSentinelInterceptor 拦截器对 Sentinel 客户端 API 的使用。

7.2 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:8080/demo/entry_demo 接口,保证 entry_demo 资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 entry_demo 资源。如下图所示:Sentinel 控制台 - 簇点链路

之后,我们给 entry_demo 资源添加一个每秒仅允许调用一次的流控规则。如下图所示:Sentinel 控制台 - 新增流控规则

④ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 FlowException",
"code": 1024
}

8. 注解支持

示例代码对应仓库:labx-04-sca-sentinel-demo01-provider

「7. Sentinel 客户端 API」小节中,我们使用 Sentinel 客户端 API,手动进行资源的保护。但是我们会发现,对代码的入侵太强,需要将业务逻辑进行修改。因此,Sentinel 提供了 @SentinelResource 注解声明自定义资源,通过 Spring AOP 拦截该注解的方法,自动调用 Sentinel 客户端 API,进行指定资源的保护。

实际上,在「4. 热点参数限流」小节里,已经使用了 @SentinelResource 注解。下面,我们来看看《Sentinel 官方文档 —— 注解支持》对它的介绍:

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT
  • blockHandler / blockHandlerClass: blockHandler对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了
  • exceptionsToIgnore:里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback:默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore:用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

下面,我们来搭建一个 Sentinel @SentinelResource 注解的示例。本着省时省力(努力偷懒)的原则,我们继续复用「2. 流量控制」小节的 lab-46-sentinel-demo 项目。

8.1 DemoController

DemoController 类中,额外添加 demo/annotations_demo 接口,使用 @SentinelResource 注解来声明资源的保护。代码如下:

@GetMapping("/annotations_demo")
@SentinelResource(value = "annotations_demo_resource",
blockHandler = "blockHandler",
fallback = "fallback")
public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {
if (id == null) {
throw new IllegalArgumentException("id 参数不允许为空");
}
return "success...";
}

// BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String blockHandler(Integer id, BlockException ex) {
return "block:" + ex.getClass().getSimpleName();
}

// Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String fallback(Integer id, Throwable throwable) {
return "fallback:" + throwable.getMessage();
}

① 在方法中,如果未传 id 参数时,抛出 IllegalArgumentException 异常。

② 在方法上,添加 @SentinelResource 注解,声明资源的保护。可能比较懵逼的是,如果有 blockHandlerfallback 属性都配置的情况下,怎么分配异常呢?实际上,Sentinel 文档中已经提到这个情况的解答

特别地,若 blockHandlerfallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

  • fallbackblockHandler 的差异点,在于 blockHandler 只能处理 BlockException 异常,fallback 能够处理所有异常。
  • 如果都配置的情况下,BlockException 异常分配给 blockHandler 处理,其它异常分配给 fallback 处理。

8.2 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:8080/demo/annotations_demo 接口,保证 annotations_demo_resource 资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 annotations_demo_resource 资源。如下图所示:Sentinel 控制台 - 簇点链路

之后,我们给 annotations_demo_resource 资源添加一个每 60 秒的异常比例是 10% 的降级规则。如下图所示:Sentinel 控制台 - 新增降级规则

③ 使用浏览器,访问 http://127.0.0.1:8080/demo/annotations_demo 接口,响应结果为 "fallback:id 参数不允许为空"。原因是,因为传入的 id 为空,所以抛出 IllegalArgumentException 异常,最终交给 #fallback(...) 方法处理。

继续不停访问 http://127.0.0.1:8080/demo/annotations_demo 接口,达到在 ② 中配置的降级规则的阀值,会响应结果为 block:DegradeException。原因是,达到降级的阀值后,抛出的是 DegradeException 异常,而该异常是 BlockingException 的子类,所以交给 #blockHandler(...) 方法处理。

9. 规则管理及推送

友情提示:本小节内容会略微难一丢丢,请保持你耐心阅读完。然后,在跟着后续小节的示例,会更加容易理解。

《Sentinel 官方文档 —— 在生产环境中使用 Sentinel》「规则管理及推送」小节,详细的介绍了 Sentinel 规则的管理与推送方式的三种模式。核心内容如下:

FROM 《Sentinel 官方文档 —— 在生产环境中使用 Sentinel》

推送模式 说明 优点 缺点
原始模式 API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource 简单,无任何依赖 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
Pull 模式 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 简单,无任何依赖;规则持久化 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
Push 模式 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 规则持久化;一致性;快速 引入第三方依赖
  • 详细的每个模式的说明,一定要认真认真认证看下文档,这对理解接下来的内容,非常重要哟。

9.1 原始模式

可能胖友会和艿艿一开始有相同的理解误区。Sentinel 控制台并不持久化规则,而是通过 sentinel-transport-simple-http 依赖提供的 HTTP API,将我们在 Sentinel 控制台编辑的规则,推送给集成 Sentinel 客户端的应用的内存中。如下图所示:原始模式

  • 因为我们引入了 sentinel-transport-simple-http 依赖,所以应用在启动的时候,会注册到 Sentinel 控制台。因此,我们在 Sentinel 控制台的「机器列表」菜单,可以看到每个应用的示例。如下图所示:Sentinel 控制台 —— 机器列表
  • 同时,sentinel-transport-simple-http 依赖提供了 HTTP API 接口,提供给 Sentinel 进行规则的推送,监控的查询。具体有哪些接口,我们来一起看下,如下图所示:sentinel-transport-simple-http API

这样一个梳理,是不是对原始模式的理解,稍微会清晰一些些了。另外,我们可以参考《Sentinel 官方文档 —— 如何使用》「规则的种类」小节,直接使用代码配置规则,通过调用 FlowRuleManager#loadRules(List<FlowRule> rules) 方法,将 Sentinel 规则加载到内存当中。

9.2 Pull 和 Push 模式

对于 Pull 模式Push 模式,都是由 Sentinel 客户端从不同的数据源,加载配置规则。并不是所有的数据源自身支持实时推送功能,因而导致 Sentinel 的规则推送模式分成非实时的 Pull 模式,和实时的 Push 模式。

《Sentinel 官方文档 —— 动态规则》中,将 Pull 和 Push 模式,统称为动态规则。同时,也提供了每种数据源的使用示例。😈 当然在下文中,我们会搭建在 Spring Boot 项目中的使用示例。

另外,考虑到更方便的配置 Sentinel 规则,需要将 Sentinel 控制台和配置中心等数据源进行集成。具体的,需要我们参考官方如下文档,进行自己实现。

FROM 《Sentinel 官方文档 —— 在生产环境中使用 Sentinel》

从 Sentinel 1.4.0 开始,Sentinel 控制台提供 DynamicRulePublisherDynamicRuleProvider 接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。改造详情可参考 应用维度规则推送示例

部署多个控制台实例时,通常需要将规则存至 DB 中,规则变更后同步向配置中心推送规则。

10. 使用 Nacos 作为数据源

示例代码对应仓库:labx-04-sca-sentinel-nacos-provider

本小节,我们使用 Nacos 作为 Sentinel 规则的数据源,并使用 Push 模式推送规则。对于 Nacos 不了解的胖友,可以先看看《Nacos 极简入门》文章。

下面,我们从「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目,复制出 labx-04-sca-sentinel-nacos-provider 项目,改造成接入 Nacos 作为数据源。

10.1 引入依赖

pom.xml 文件中,额外引入相关依赖。

<!-- Sentinel 对 Nacos 作为数据源的支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

引入 sentinel-datasource-nacos 依赖,实现 Sentinel 对 Nacos 作为数据源的支持。

10.2 配置文件

修改 application.yaml 配置文件,添加 Sentinel 使用 Nacos 作为数据源。完整配置如下:

spring:
application:
name: demo-provider

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*
# Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置
datasource:
ds1:
# 对应 DataSourcePropertiesConfiguration 类
nacos:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: # Nacos 命名空间
group-id: DEFAULT_GROUP # Nacos 分组
data-id: ${spring.application.name}-flow-rule # Nacos 配置集编号
data-type: json # 数据格式
rule-type: FLOW # 规则类型

通过添加 spring.cloud.sentinel.datasource 配置项,设置接入的 Sentinel 规则的数据源。注意它是一个 Map 类型,其中:

key:为数据源名,可自定义,无特殊含义。这里我们添加了一个 ds1,如果胖友想要更多数据源,可以继续添加噢。

value:为数据源的具体配置,对应 DataSourcePropertiesConfiguration 类,可以选择 filenacoszkapolloredis 任一作为数据的数据源。这里我们选择 nacos 来接入 Nacos 作为数据源。

  • rule-type:数据源对应的 Sentinel 规则类型,在 RuleType 类枚举。这里我们设置了 FLOW 对应流量控制的规则。
  • data-type:数据源的数据格式,默认为 json。这里我们设置了 json,所以稍后创建的 Nacos 配置集的数据格式要为 JSON
  • server-addr:Nacos 服务器地址。
  • namespace:Nacos 分组。
  • data-id:Nacos 配置集编号。推荐配置集编号的命名规则为 ${applicationName}-${ruleType},因此这里我们设置为 demo-provider-flow-rule,即 demo-provider 应用的流控规则。

10.3 创建 Nacos 配置集

理论来说,我们需要改造 Sentinel 控制台的代码,将 Sentinel 接入 Nacos 作为规则的数据源。但是考虑到涉及的内容较多,本文暂时跳过,感兴趣的胖友,可以阅读应用维度规则推送示例文章。

咳咳咳,理论来说,Sentinel 控制台应该内置了对 Nacos 数据源的接入。

也因此,我门直接在 Nacos 中,创建一个配置集 demo-provider-flow-rule,具体内容如下图:创建 Nacos 配置集

配置内容中,我们设置了一个针对 /demo/echo 资源,每秒允许访问 5 次。每个字段的说明如下:

[
{
"resource": "/demo/echo",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
  • 注意是数组哈~

FROM 《Sentinel 控制规则 —— 流量控制》

  • resource:资源名,即限流规则的作用对象
  • count: 限流阈值
  • grade: 限流阈值类型(QPS 或并发线程数)
  • limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  • strategy: 调用关系限流策略
  • controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

10.4 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 Nacos 数据源加载而来的。如下图所示:Sentinel 控制台 - 流控规则

③ 使用浏览器,快速访问 http://127.0.0.1:8080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 FlowException",
"code": 1024
}

11. 使用 Apollo 作为数据源

示例代码对应仓库:labx-04-sca-sentinel-apollo-provider

本小节,我们使用 Apollo 作为 Sentinel 规则的数据源,并使用 Push 模式推送规则。对于 Nacos 不了解的胖友,可以先看看《Apollo 极简入门》文章。

下面,我们从「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目,复制出 labx-04-sca-sentinel-apollo-provider 项目,改造成接入 Apollo 作为数据源。

11.1 引入依赖

pom.xml 文件中,额外引入相关依赖。

<!-- Sentinel 对 Apollo 作为数据源的支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
</dependency>

引入 sentinel-datasource-apollo 依赖,实现 Sentinel 对 Apollo 作为数据源的支持。

11.2 配置文件

修改 application.yaml 配置文件,添加 Sentinel 使用 Apollo 作为数据源。完整配置如下:

server:
port: 18080 # 服务器端口,设置为 18080 避免和本地的 Apollo 端口冲突

# Apollo 相关配置项
app:
id: ${spring.application.name} # 使用的 Apollo 的项目(应用)编号
apollo:
meta: http://127.0.0.1:8080 # Apollo Meta Server 地址
bootstrap:
enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。
eagerLoad:
enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。
namespaces: application # 使用的 Apollo 的命名空间,默认为 application。

spring:
application:
name: demo-provider

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*
# Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置
datasource:
ds1:
# 对应 DataSourcePropertiesConfiguration 类
apollo:
namespaceName: application # Apollo 命名空间
flowRulesKey: sentinel.flow-rule # Apollo 配置 key
data-type: json # 数据格式
rule-type: FLOW # 规则类型

📚 纯 Apollo 相关配置

appapollo 配置项,是 Apollo 相关的配置。这里我们使用 Apollo 应用为 demo-provider。其它的配置项,胖友看注释即可,更详细的可以阅读《芋道 Spring Boot 配置中心 Apollo 入门》文章。

📚 Sentinel 数据源相关配置

通过添加 spring.cloud.sentinel.datasource 配置项,设置接入的 Sentinel 规则的数据源。注意它是一个 Map 类型,其中:

key:为数据源名,可自定义,无特殊含义。这里我们添加了一个 ds1,如果胖友想要更多数据源,可以继续添加噢。

value:为数据源的具体配置,对应 DataSourcePropertiesConfiguration 类,可以选择 filenacoszkapolloredis 任一作为数据的数据源。这里我们选择 apollo 来接入 Apollo 作为数据源。

  • rule-type:数据源对应的 Sentinel 规则类型,在 RuleType 类枚举。这里我们设置了 FLOW 对应流量控制的规则。
  • data-type:数据源的数据格式,默认为 json。这里我们设置了 json,所以稍后创建的 Nacos 配置集的数据格式要为 JSON
  • namespaceName:Apollo 命名空间。
  • flowRulesKey:Apollo 配置 Key。推荐配置项的 Key 的命名规则为 sentinel.${ruleType},因此这里我们设置为 sentinel.flow-rule,即当前应用的流控规则。

11.4 创建 Apollo 配置

理论来说,我们需要改造 Sentinel 控制台的代码,将 Sentinel 接入 Apollo 作为规则的数据源。但是考虑到涉及的内容较多,本文暂时跳过,感兴趣的胖友,可以阅读应用维度规则推送示例文章。

咳咳咳,理论来说,Sentinel 控制台应该内置了对 Apollo 数据源的接入。

也因此,我门直接在 Apollo 中,创建一个配置项 sentinel.flow-rule,具体内容如下图:创建 Apollo 配置项

在配置项的 Value 中,我们设置了一个针对 /demo/echo 资源,每秒允许访问 5 次。每个字段的说明如下:

[
{
"resource": "/demo/echo",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
  • 注意是数组哈~

FROM 《Sentinel 控制规则 —— 流量控制》

  • resource:资源名,即限流规则的作用对象
  • count: 限流阈值
  • grade: 限流阈值类型(QPS 或并发线程数)
  • limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  • strategy: 调用关系限流策略
  • controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

11.5 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 Apollo 数据源加载而来的。如下图所示:Sentinel 控制台 - 流控规则

③ 使用浏览器,快速访问 http://127.0.0.1:18080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 FlowException",
"code": 1024
}

12. 使用 File 作为数据源

示例代码对应仓库:lab-46-sentinel-demo-file

本小节,我们使用 File(文件) 作为 Sentinel 规则的数据源。注意,生产环境下,不建议使用 File 作为数据源。

下面,我们从「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目,复制出 labx-04-sca-sentinel-apollo-provider 项目,改造成接入 File 作为数据源。

12.1 配置文件

修改 application.yaml 配置文件,添加 Sentinel 使用 File 作为数据源。完整配置如下:

spring:
application:
name: demo-provider

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*
# Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置
datasource:
ds1:
# 对应 DataSourcePropertiesConfiguration 类
file:
file: /Users/yunai/Sentinel/demo-provider/flow-rule.json # 配置规则所在文件。
recommendRefreshMs: 3000 # 定时读取实现刷新,默认为 3000 毫秒。
data-type: json # 数据格式
rule-type: FLOW # 规则类型

通过添加 spring.cloud.sentinel.datasource 配置项,设置接入的 Sentinel 规则的数据源。注意它是一个 Map 类型,其中:

key:为数据源名,可自定义,无特殊含义。这里我们添加了一个 ds1,如果胖友想要更多数据源,可以继续添加噢。

value:为数据源的具体配置,对应 DataSourcePropertiesConfiguration 类,可以选择 filenacoszkapolloredis 任一作为数据的数据源。这里我们选择 file 来接入 Nacos 作为数据源。

  • rule-type:数据源对应的 Sentinel 规则类型,在 RuleType 类枚举。这里我们设置了 FLOW 对应流量控制的规则。
  • data-type:数据源的数据格式,默认为 json。这里我们设置了 json,所以稍后创建的 Nacos 配置集的数据格式要为 JSON
  • server-addr:Nacos 服务器地址。
  • file:配置规则所在文件。
  • recommendRefreshMs:定时读取实现刷新,默认为 3000 毫秒。

12.2 创建 File 文件

创建 /Users/yunai/Sentinel/demo-provider/flow-rule.json 文件,存放应用 demo-provider 的 Sentinel 流量控制的规则。其内容如下:

[
{
"resource": "/demo/echo",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
  • 注意是数组哈~

FROM 《Sentinel 控制规则 —— 流量控制》

  • resource:资源名,即限流规则的作用对象
  • count: 限流阈值
  • grade: 限流阈值类型(QPS 或并发线程数)
  • limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  • strategy: 调用关系限流策略
  • controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

12.3 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 File 数据源加载而来的。如下图所示:Sentinel 控制台 - 流控规则

③ 使用浏览器,快速访问 http://127.0.0.1:8080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回如下 JSON 字符串:

{
"msg": "请求被拦截,拦截类型为 FlowException",
"code": 1024
}

旁白君:File 数据源还支持 Pull 模式推送规则的持久化,不过实际基本不会使用到。如果感兴趣的胖友,可以阅读《芋道 Spring Boot 服务容错 Sentinel 入门》文章的「12. 使用 File 作为数据源」小节。

13. 集群流控

艿艿暂时没有去研究 Sentinel 的集群流控功能,主要看 Token Server 暂时未提供高可用方案,这个上到生产肯定是有蛮大风险的。感兴趣的胖友,可以先阅读如下文章:

14. 整合 Feign

示例代码对应仓库:

本小节我们来进行 Feign 和 Sentinel 的整合,该功能由 Spring Cloud Alibaba Sentinel 的 feign 模块提供。

Feign 是一款声明式 HTTP 客户端,可以更快捷、更优雅的实现 HTTP API 调用。不了解的胖友,推荐阅读下《芋道 Spring Cloud 声明式调用 Feign 入门》文章。

下面,我们搭建一个服务消费者项目 labx-04-sca-sentinel-feign-consumer,并将「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目作为服务提供者,使用 Feign 进行 HTTP API 调用。最终示例项目如下图所示:项目结构

14.1 引入依赖

创建服务消费者项目 labx-04-sca-sentinel-feign-consumer,在 pom.xml 文件中,主要引入 Sentinel 和 Feign 相关依赖。代码如下:

<?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>
<artifactId>labx-04</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-04-sca-sentinel-feign-consumer</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- 引入 Spring Cloud OpenFeign 相关依赖,使用 OpenFeign 提供声明式调用,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>

</project>

每个依赖的作用,胖友可以看艿艿在其上添加的注释。

14.2 配置文件

创建 application.yaml 配置类,添加相应配置项。配置如下:

spring:
application:
name: demo-consumer

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*

server:
port: 8081

feign:
sentinel:
enabled: true # 开启 Sentinel 对 Feign 的支持,默认为 false 关闭。

重点feign.sentinel.enabled 配置项,设置为 true,开启 Sentinel 对 Feign 的支持。

14.3 DemoProviderFeignClient

创建 DemoProviderFeignClient 接口,实现对服务 demo-provider 声明式调用。代码如下:

@FeignClient(name = "demo-provider", url = "http://127.0.0.1:8080",
fallbackFactory = DemoProviderFeignClientFallbackFactory.class)
public interface DemoProviderFeignClient {

@GetMapping("/demo/echo")
String echo();

}

通过 @FeignClient 注解,声明一个 FeignClient 客户端。

name 属性,设置 FeignClient 客户端的名字。

url 属性,设置调用服务的地址。因为我们没有引入注册中心,所以我们直接设置稍后启动的服务 demo-provider 的地址。

fallbackFactory 属性,设置 fallback 工厂类。fallback 的作用是,用于在 HTTP 调动失败而抛出异常的时候,提供 fallback 处理逻辑。

友情提示:Feign 和 Sentinel 进行整合的时候,fallback 并不是必须条件,主要看是否想要提供 fallback 处理逻辑。

14.3.1 DemoProviderFeignClientFallbackFactory

创建 DemoProviderFeignClientFallbackFactory 类,用于创建 DemoProviderFeignClientFallback 的工厂类。代码如下:

@Component
public class DemoProviderFeignClientFallbackFactory implements FallbackFactory<DemoProviderFeignClientFallback> {

@Override
public DemoProviderFeignClientFallback create(Throwable throwable) {
// 可以给 DemoProviderFeignClientFallback 提供具体的 throwable 异常
return new DemoProviderFeignClientFallback(throwable);
}

}

注意,类上需要添加 @Component 注解,因为 @FeignClient 注解的 fallbackFactory 属性,是通过从 Spring 中获取 fallbackFactory 对应类型的 Bean。

14.3.2 DemoProviderFeignClientFallback

创建 DemoProviderFeignClientFallback 类,提供 DemoProviderFeignClient 的 fallback 处理逻辑。代码如下:

public class DemoProviderFeignClientFallback implements DemoProviderFeignClient {

private Throwable throwable;

public DemoProviderFeignClientFallback(Throwable throwable) {
this.throwable = throwable;
}

@Override
public String echo() {
return "fallback:" + throwable.getClass().getSimpleName();
}

}

注意,要实现 DemoProviderFeignClient 接口,这样每个实现方法,能够一一对应,进行 fallback 处理逻辑。

14.4 ConsumerController

创建 ConsumerController 类,提供一个通过 Feign 调用服务提供者的 HTTP 接口。代码如下:

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

@Autowired
private DemoProviderFeignClient demoProviderFeignClient;

@GetMapping("/echo")
public String echo() {
return demoProviderFeignClient.echo();
}

}

14.5 DemoConsumerApplication

创建 DemoConsumerApplication 类,创建应用启动类。代码如下:

@SpringBootApplication
@EnableFeignClients
public class DemoConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}

}

@EnableFeignClients 注解,添加在类上,声明开启 Feign 客户端的功能。

14.6 简单测试

① 通过「2. 流量控制」小节的 DemoProviderApplication 启动服务提供者。使用 DemoConsumerApplication 启动服务消费者

② 访问服务消费者http://127.0.0.1:8081/consumer/echo 接口,保证相关资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到看到 Feign 的服务提供者产生的 GET:http://127.0.0.1:8080/demo/echo 资源。如下图所示:Sentinel 控制台 - 簇点链路

点击 GET:http://127.0.0.1:8080/demo/echo 资源所在列的「流控」按钮,弹出「新增流控规则」。填写流控规则,如下图所示:Sentinel 控制台 - 新增流控规则

  • 这里,我们创建的是比较简单的规则,仅允许该资源被每秒调用一次。

④ 使用浏览器,访问 http://127.0.0.1:8081/consumer/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,返回结果为 fallback:FlowException。这说明 Feign 和 Sentinel 的整合成功,并进入 DemoProviderFeignClient 的 fallback 处理逻辑。

15. 整合 RestTemplate

本小节我们来进行 Feign 和 RestTemplate 的整合,该功能由 Spring Cloud Alibaba Sentinel 的 custom 模块提供。

RestTemplate 是 Spring 提供的用于访问 Rest 服务的 HTTP 客户端,提供了多种可以便捷调用远程 HTTP 服务的方法,能够大大提高客户端的开发效率。

下面,我们搭建一个服务消费者项目 labx-04-sca-sentinel-resttemplate-consumer,并将「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目作为服务提供者,使用 Feign 进行 HTTP API 调用。最终示例项目如下图所示:项目结构

15.1 引入依赖

示例代码对应仓库:

创建服务消费者项目 labx-04-sca-sentinel-resttemplate-consumer,在 pom.xml 文件中,主要引入 Sentinel 相关依赖。代码如下:

<?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>
<artifactId>labx-04</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-04-sca-sentinel-resttemplate-consumer</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>

</project>

每个依赖的作用,胖友可以看艿艿在其上添加的注释。

15.2 配置文件

创建 application.yaml 配置类,添加相应配置项。配置如下:

spring:
application:
name: demo-consumer

cloud:
# Sentinel 配置项,对应 SentinelProperties 配置属性类
sentinel:
enabled: true # 是否开启。默认为 true 开启
eager: true # 是否饥饿加载。默认为 false 关闭
transport:
dashboard: 127.0.0.1:7070 # Sentinel 控制台地址
filter:
url-patterns: /** # 拦截请求的地址。默认为 /*

server:
port: 8081

resttemplate:
sentinel:
enabled: true # 开启 Sentinel 对 Feign 的支持,默认为 true 开启。

重点resttemplate.sentinel.enabled 配置项,设置为 true,开启 Sentinel 对 RestTemplate 的支持。不过因为默认就为 true,所以可以不配置。

15.3 RestTemplateConfiguration

创建 RestTemplateConfiguration 配置类,创建 RestTemplate Bean。代码如下:

@Configuration
public class RestTemplateConfiguration {

@Bean
@SentinelRestTemplate
public RestTemplate restTemplate() {
return new RestTemplate();
}

}

重点! 通过 @SentinelRestTemplate 注解,声明 Sentinel 对 RestTemplate 的支持。

另外,@SentinelRestTemplate 注解提供了 blockHandlerblockHandlerClassfallbackfallbackClass 属性,作用和 @SentinelResource 注解是一致的。

15.4 ConsumerController

创建 ConsumerController 类,提供一个通过 RestTemplate 调用服务提供者的 HTTP 接口。代码如下:

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

@Autowired
private RestTemplate restTemplate;

@GetMapping("/echo")
public String echo() {
return restTemplate.getForObject("http://127.0.0.1:8080/demo/echo", String.class);
}

}

15.5 DemoConsumerApplication

创建 DemoConsumerApplication 类,创建应用启动类。代码如下:

@SpringBootApplication
public class DemoConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}

}

15.6 简单测试

① 通过「2. 流量控制」小节的 DemoProviderApplication 启动服务提供者。使用 DemoConsumerApplication 启动服务消费者

② 访问服务消费者http://127.0.0.1:8081/consumer/echo 接口,保证相关资源的初始化。

③ 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到看到 Feign 的服务提供者产生的 GET:http://127.0.0.1:8080/demo/echo 资源。如下图所示:Sentinel 控制台 - 簇点链路

点击 GET:http://127.0.0.1:8080/demo/echo 资源所在列的「流控」按钮,弹出「新增流控规则」。填写流控规则,如下图所示:Sentinel 控制台 - 新增流控规则

  • 这里,我们创建的是比较简单的规则,仅允许该资源被每秒调用一次。

④ 使用浏览器,访问 http://127.0.0.1:8081/consumer/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,返回结果为 RestTemplate request block by sentinel。这说明 RestTemplate 和 Sentinel 的整合成功,并返回 Sentinel 对 RestTemplate 默认的 block 的响应

16. 监控端点

示例代码对应仓库:labx-04-sca-sentinel-actuator-provider

Spring Cloud Alibaba Sentinel 的 endpoint 模块,基于 Spring Boot Actuator,提供了自定义监控端点 sentinel,获取 Sentinel 的配置项,和各种规则

同时, Sentinel 拓展了 Spring Boot Actuator 内置的 health 端点,通过自定义的 SentinelHealthIndicator,获取和 Sentinel 控制台和数据源的连接状态。

友情提示:对 Spring Boot Actuator 不了解的胖友,可以后续阅读《芋道 Spring Boot 监控端点 Actuator 入门》文章。

下面,我们从「2. 流量控制」小节的 labx-04-sca-sentinel-demo01-provider 项目,复制出 labx-04-sca-sentinel-actuator-provider 项目,快速搭建 Sentinel 监控端点的使用示例。

16.1 引入依赖

pom.xml 文件中,额外引入 Spring Boot Actuator 相关依赖。代码如下:

<!-- 实现对 Actuator 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

16.2 配置文件

修改 application.yaml 配置文件,额外增加 Spring Boot Actuator 配置项。配置如下:

management:
endpoints:
web:
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
endpoint:
# Health 端点配置项,对应 HealthProperties 配置类
health:
enabled: true # 是否开启。默认为 true 开启。
show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。

每个配置项的作用,胖友看下艿艿添加的注释。如果还不理解的话,后续看下《芋道 Spring Boot 监控端点 Actuator 入门》文章。

16.3 简单测试

① 使用 DemoProviderApplication 启动示例应用。

② 访问应用的 nacos-discovery 监控端点 http://127.0.0.1:8080/actuator/sentinel,返回结果如下图:`sentinel` 监控端点

③ 访问应用的 health 监控端点 http://127.0.0.1:8080/actuator/health,返回结果如下图:`health` 监控端点

17. 更多的配置项信息

Spring Cloud Alibaba Sentinel 提供的配置项挺多的,我们参考文档将配置项一起梳理下。

Spring Cloud Alibaba Sentinel 提供了这些配置选项:

Sentinel 基础配置

配置项 说明 默认值
spring.application.name or project.name Sentinel项目名
spring.cloud.sentinel.enabled Sentinel自动化配置是否生效 true
spring.cloud.sentinel.eager 是否提前触发 Sentinel 初始化 false
spring.cloud.sentinel.flow.cold-factor WarmUp 模式中的 冷启动因子 3
spring.cloud.sentinel.log.dir Sentinel 日志文件所在的目录
spring.cloud.sentinel.log.switch-pid Sentinel 日志文件名是否需要带上 pid false

Sentinel 控制台相关

配置项 说明 默认值
spring.cloud.sentinel.transport.port 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer 8719
spring.cloud.sentinel.transport.dashboard Sentinel 控制台地址
spring.cloud.sentinel.transport.heartbeat-interval-ms 应用与Sentinel控制台的心跳间隔时间
spring.cloud.sentinel.transport.client-ip 此配置的客户端IP将被注册到 Sentinel Server 端
spring.cloud.sentinel.metric.charset metric文件字符集 UTF-8
spring.cloud.sentinel.metric.file-single-size Sentinel metric 单个文件的大小
spring.cloud.sentinel.metric.file-total-count Sentinel metric 总文件数量

Servlet 相关

配置项 说明 默认值
spring.cloud.sentinel.filter.order Servlet Filter的加载顺序。Starter内部会构造这个filter Integer.MIN_VALUE
spring.cloud.sentinel.filter.url-patterns 数据类型是数组。表示Servlet Filter的url pattern集合 /*
spring.cloud.sentinel.filter.enabled Enable to instance CommonFilter true
spring.cloud.sentinel.servlet.block-page 自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL

网关 Zuul 相关

配置项 说明 默认值
spring.cloud.sentinel.zuul.order.pre SentinelZuulPreFilter 的 order 10000
spring.cloud.sentinel.zuul.order.post SentinelZuulPostFilter 的 order 1000
spring.cloud.sentinel.zuul.order.error SentinelZuulErrorFilter 的 order -1

网关 Spring Cloud Gateway 相关

配置项 说明 默认值
spring.cloud.sentinel.scg.fallback.mode Spring Cloud Gateway 流控处理逻辑 (选择 redirect or response)
spring.cloud.sentinel.scg.fallback.redirect Spring Cloud Gateway 响应模式为 ‘redirect’ 模式对应的重定向 URL
spring.cloud.sentinel.scg.fallback.response-body Spring Cloud Gateway 响应模式为 ‘response’ 模式对应的响应内容
spring.cloud.sentinel.scg.fallback.response-status Spring Cloud Gateway 响应模式为 ‘response’ 模式对应的响应码 429
spring.cloud.sentinel.scg.fallback.content-type Spring Cloud Gateway 响应模式为 ‘response’ 模式对应的 content-type application/json

666. 彩蛋

至此,我们已经完成 Spring Cloud Alibaba Sentinel 的学习。如下是 Sentinel 相关的官方文档:

另外,想要在 Spring Boot 项目中使用 Sentinel 进行服务容错的胖友,可以阅读《芋道 Spring Boot 服务容错 Sentinel 入门》文章。

文章目录
  1. 1. 1. 概述
  2. 2. 2. 流量控制
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 配置文件
    3. 2.3. 2.3 BlockException 处理器
    4. 2.4. 2.4 DemoController
    5. 2.5. 2.5 DemoProviderApplication
    6. 2.6. 2.6 简单测试
  3. 3. 3. 熔断降级
    1. 3.1. 3.1 DemoController
    2. 3.2. 3.2 简单测试
  4. 4. 4. 热点参数限流
    1. 4.1. 4.1 DemoController
    2. 4.2. 4.2 简单测试
  5. 5. 5. 系统自适应限流
    1. 5.1. 5.1 简单测试
  6. 6. 6. 黑白名单控制
    1. 6.1. 6.1 RequestOriginParser
    2. 6.2. 6.2 简单测试
  7. 7. 7. Sentinel 客户端 API
    1. 7.1. 7.1 DemoController
    2. 7.2. 7.2 简单测试
  8. 8. 8. 注解支持
    1. 8.1. 8.1 DemoController
    2. 8.2. 8.2 简单测试
  9. 9. 9. 规则管理及推送
    1. 9.1. 9.1 原始模式
    2. 9.2. 9.2 Pull 和 Push 模式
  10. 10. 10. 使用 Nacos 作为数据源
    1. 10.1. 10.1 引入依赖
    2. 10.2. 10.2 配置文件
    3. 10.3. 10.3 创建 Nacos 配置集
    4. 10.4. 10.4 简单测试
  11. 11. 11. 使用 Apollo 作为数据源
    1. 11.1. 11.1 引入依赖
    2. 11.2. 11.2 配置文件
    3. 11.3. 11.4 创建 Apollo 配置
    4. 11.4. 11.5 简单测试
  12. 12. 12. 使用 File 作为数据源
    1. 12.1. 12.1 配置文件
    2. 12.2. 12.2 创建 File 文件
    3. 12.3. 12.3 简单测试
  13. 13. 13. 集群流控
  14. 14. 14. 整合 Feign
    1. 14.1. 14.1 引入依赖
    2. 14.2. 14.2 配置文件
    3. 14.3. 14.3 DemoProviderFeignClient
      1. 14.3.1. 14.3.1 DemoProviderFeignClientFallbackFactory
      2. 14.3.2. 14.3.2 DemoProviderFeignClientFallback
    4. 14.4. 14.4 ConsumerController
    5. 14.5. 14.5 DemoConsumerApplication
    6. 14.6. 14.6 简单测试
  15. 15. 15. 整合 RestTemplate
    1. 15.1. 15.1 引入依赖
    2. 15.2. 15.2 配置文件
    3. 15.3. 15.3 RestTemplateConfiguration
    4. 15.4. 15.4 ConsumerController
    5. 15.5. 15.5 DemoConsumerApplication
    6. 15.6. 15.6 简单测试
  16. 16. 16. 监控端点
    1. 16.1. 16.1 引入依赖
    2. 16.2. 16.2 配置文件
    3. 16.3. 16.3 简单测试
  17. 17. 17. 更多的配置项信息
  18. 18. 666. 彩蛋