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 的大量依赖,非常方便,如下图所示:

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 的限流是比较相似的。不过很大的差异是,热点参数限流是针对每个参数,分别计数来限流。举个例子,在当前示例的热点规则下:

  • 针对每个 id 对应的 http://127.0.0.1:8080/demo/product_info?id=${id} 接口,在每 60 秒内,分别允许访问 10 次。
  • 针对 id = 1 的情况,作为特殊(例外)配置,在每 60 秒内,仅仅允许访问 1 次。

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

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 / blockHandlerClassblockHandler对应处理 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 处理逻辑。若未配置 blockHandlerfallback 和 defaultFallback,则被限流降级时会将 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 注解,声明资源的保护。可能比较懵逼的是,如果有 blockHandler 和 fallback 属性都配置的情况下,怎么分配异常呢?实际上,Sentinel 文档中已经提到这个情况的解答

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

  • fallback 和 blockHandler 的差异点,在于 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 控制台提供 DynamicRulePublisher 和 DynamicRuleProvider 接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。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 相关配置

在 app 和 apollo 配置项,是 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();
    }

}

注意,类上需要添加 @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,返回结果如下图:监控端点

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

17. 更多的配置项信息

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

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

Sentinel 基础配置

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

Sentinel 控制台相关

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

Servlet 相关

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

网关 Zuul 相关

配置项说明默认值
spring.cloud.sentinel.zuul.order.preSentinelZuulPreFilter 的 order10000
spring.cloud.sentinel.zuul.order.postSentinelZuulPostFilter 的 order1000
spring.cloud.sentinel.zuul.order.errorSentinelZuulErrorFilter 的 order-1

网关 Spring Cloud Gateway 相关

配置项说明默认值
spring.cloud.sentinel.scg.fallback.modeSpring Cloud Gateway 流控处理逻辑 (选择 redirect or response) 
spring.cloud.sentinel.scg.fallback.redirectSpring Cloud Gateway 响应模式为 'redirect' 模式对应的重定向 URL 
spring.cloud.sentinel.scg.fallback.response-bodySpring Cloud Gateway 响应模式为 'response' 模式对应的响应内容 
spring.cloud.sentinel.scg.fallback.response-statusSpring Cloud Gateway 响应模式为 'response' 模式对应的响应码429
spring.cloud.sentinel.scg.fallback.content-typeSpring Cloud Gateway 响应模式为 'response' 模式对应的 content-typeapplication/json

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

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐