Spring Boot 服务容错 Sentinel 入门

1. 概述

在《Sentinel 极简入门》中,我们简单了解了 Sentinel,并搭建了 Sentinel 控制台。如果还没看的胖友,可以先看看该文的「1. 概述」和「2. 控制台」小节。

Sentinel 功能比较强大,同时胖友可能对服务容错可能比较陌生,所以我们跟着示例,一个一个来学习噢。

2. 流量控制

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

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

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

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

设计理念

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

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

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

下面,我们来搭建一个流量控制的 Spring Boot 示例。

2.1 引入依赖

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



    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.2.RELEASE
         
    
    4.0.0

    lab-46-sentinel-demo

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.alibaba.csp
            sentinel-core
            1.7.1
        
        
        
            com.alibaba.csp
            sentinel-transport-simple-http
            1.7.1
        
        
        
            com.alibaba.csp
            sentinel-spring-webmvc-adapter
            1.7.1
        
    

2.2 SpringMvcConfiguration

在 cn.iocoder.springboot.lab46.sentineldemo.config 包下,创建 SpringMvcConfiguration 配置类,自定义 sentinel-spring-webmvc-adapter 提供的拦截器。代码如下:

@Configuration
public class SpringMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Add Sentinel interceptor
//        addSentinelWebTotalInterceptor(registry);
        addSentinelWebInterceptor(registry);
    }

    private void addSentinelWebInterceptor(InterceptorRegistry registry) {
        // <1.1> 创建 SentinelWebMvcConfig 对象
        SentinelWebMvcConfig config = new SentinelWebMvcConfig();
        config.setHttpMethodSpecify(true); // <1.2> 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。
        // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // <1.3> 设置 BlockException 处理器。

        // <2> 添加 SentinelWebInterceptor 拦截器
        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
    }

    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {
        // <1> 创建 SentinelWebMvcTotalConfig 对象
        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();

        // <2> 添加 SentinelWebTotalInterceptor 拦截器
        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
    }

}
  • #addSentinelWebTotalInterceptor(InterceptorRegistry registry) 方法,添加 SentinelWebTotalInterceptor 拦截器。
  • #addSentinelWebInterceptor(InterceptorRegistry registry) 方法,添加 SentinelWebInterceptor 拦截器。

SentinelWebInterceptor 拦截器,针对每个 URL 进行流量控制。

  • <1.1> 处,创建 SentinelWebMvcConfig 对象,用于作为 SentinelWebInterceptor 拦截器的配置。

  • <1.2> 处,设置 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。这里有一个非常重要的概念,就是“资源”。

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

    资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

    只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。 * 对于 SentinelWebInterceptor 拦截器来说,将 URL + Method 作为一个资源,进行流量控制。具体的,可以看看 SentinelWebInterceptor#getResourceName(HttpServletRequest request) 方法的代码。

  • <1.3> 处,设置 BlockException 的处理器。Sentinel 在流量控制时,当请求到达阀值后,会抛出 BlockException 异常。此时,可以通过定义 BlockExceptionHandler 去处理。这里,我们使用 SpringMVC 提供的全局异常处理机制,具体可见「2.3 GlobalExceptionHandler」。

  • <2> 处,添加 SentinelWebInterceptor 拦截器到 InterceptorRegistry 中。

SentinelWebTotalInterceptor 拦截器,针对全局 URL 进行流量控制。简单来说,所有 URL 合计流量,全局统一进行控制。

  • <1> 处,创建 SentinelWebMvcTotalConfig 对象,用于作为 SentinelWebTotalInterceptor 拦截器的配置。
  • <2> 处,添加 SentinelWebTotalInterceptor 拦截器到 InterceptorRegistry 中。

2.3 GlobalExceptionHandler

在 cn.iocoder.springboot.lab46.sentineldemo.web 包下,创建 GlobalExceptionHandler 配置类,自定义 sentinel-spring-webmvc-adapter 提供的拦截器。代码如下:

@ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller")
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = BlockException.class)
    public String blockExceptionHandler(BlockException blockException) {
        return "请求过于频繁";
    }

}
  • 在 #blockExceptionHandler(...) 方法中,我们处理 BlockException 异常。因为这里是示例,所以处理的比较简单。胖友可以看看《芋道 Spring Boot SpringMVC 入门》的「5. 全局异常处理」小节。

2.4 DemoController

在 cn.iocoder.springboot.lab46.sentineldemo.controller 包下,创建 DemoController 类,提供稍后测试流量控制的示例 API。代码如下:

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

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

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

}

2.5 Sentinel 配置文件

在 resources 目录下,创建 Sentinel 自定义的sentinel.properties 配置文件。内容如下:

csp.sentinel.dashboard.server=127.0.0.1:7070
  • csp.sentinel.dashboard.server 配置项,设置 Sentinel 控制台地址。
  • 更多其它配置项,可见《Sentinel 官方文档 —— 启动配置项》。

2.6 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        //  设置系统属性 project.name,提供给 Sentinel 读取
        System.setProperty("project.name", "demo-application");

        // 启动 Spring Boot 应用
        SpringApplication.run(Application.class, args);
    }

}
  • 在  处,设置系统属性 project.name,提供给 Sentinel 读取。比较特殊,该配置项无法在 csp.sentinel.dashboard.server 配置文件中设置。

2.7 简单测试

启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。此时,控制台输出日志如下:

INFO: log output type is: file
INFO: log charset is: utf-8
INFO: log base dir is: /Users/yunai/logs/csp/
INFO: log name use pid is: false

② 使用浏览器,访问下 http://127.0.0.1:7070/ 地址,进入 Sentinel 控制台。此时,我们可以看到 demo-application 应用。如下图所示:Spring Boot 服务容错 Sentinel 入门_第1张图片

③ 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口 10 次。然后点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口的请求情况。如下图所示:Spring Boot 服务容错 Sentinel 入门_第2张图片

④ 点击 Sentinel 控制台的「簇点链路」菜单,可以看到 GET:/demo/echo 资源。如下图所示:Spring Boot 服务容错 Sentinel 入门_第3张图片

⑤ 点击 GET:/demo/echo 资源所在列的「流控」按钮,弹出「新增流控规则」。填写流控规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第4张图片

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

⑥ 点击「新增」按钮,完成流控规则的添加。此时,会自动跳转到「流控规则」菜单。如下图所示:Spring Boot 服务容错 Sentinel 入门_第5张图片

⑦ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Spring Boot 服务容错 Sentinel 入门_第6张图片

3. 熔断降级

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

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

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

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

设计理念

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

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

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

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

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

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

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

3.1 DemoController

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

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

3.2 简单测试

重新启动 Spring Boot 应用。

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

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/sleep 接口。因为 Sentinel 客户端是懒加载的。

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

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

之后,点击 GET:/demo/sleep 资源所在列的「降级」按钮,弹出「新增降级规则」。填写降级规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第7张图片

  • 这里,我们创建的是比较简单的规则,当 GET:/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,则结束熔断状态后仍可能再进入熔断状态。

③ 点击「新增」按钮,完成降级规则的添加。此时,会自动跳转到「降级规则」菜单。如下图所示:Spring Boot 服务容错 Sentinel 入门_第8张图片

④ 使用浏览器,访问 http://127.0.0.1:8080/demo/sleep 接口 6 次,就会有被 Sentinel 服务降级而拒绝,最终返回 "请求过于频繁"

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Spring Boot 服务容错 Sentinel 入门_第9张图片

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

4. 热点参数限流

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

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

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

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

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

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

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

下面,我们来搭建一个热点参数限流的 Spring Boot 示例。本着省时省力(努力偷懒)的原则,我们继续复用「2. 流量控制」小节的 lab-46-sentinel-demo 项目。

4.1 引入依赖

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



    com.alibaba.csp
    sentinel-parameter-flow-control
    1.7.1



    com.alibaba.csp
    sentinel-annotation-aspectj
    1.7.1
  • 引入 sentinel-parameter-flow-control 依赖,实现 Sentinel 对【热点参数限流】的支持。
  • 引入 sentinel-annotation-aspectj 依赖,实现 Sentinel 对 Spring AOP 的推展。稍后我们会使用到 Sentinel 提供的 @SentinelResource 注解声明自定义资源,通过 Spring AOP 拦截该注解的方法,从而实现自定义资源的 Sentinel 的处理逻辑。

4.2 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 接口生成的 GET:/demo/product_info 呢?

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

4.3 简单测试

重新启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/product_info?id=1 接口。因为 Sentinel 客户端是懒加载的。

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

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

之后,点击 demo_product_info_hot 资源所在列的「热点」按钮,弹出「新增热点规则」。填写热点规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第10张图片

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

③ 点击「新增」按钮,完成热点规则的添加。此时,会自动跳转到「热点规则」菜单。如下图所示:Spring Boot 服务容错 Sentinel 入门_第11张图片

之后,点击 demo_product_info_hot 资源所在列的「编辑」按钮,弹出「编辑热点规则」。填写热点规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第12张图片

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

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

④ 使用浏览器,访问 http://127.0.0.1:8080/demo/product_info?id=1 接口 2 次,就会有被 Sentinel 热点参数限流而拒绝,最终返回 "请求过于频繁"

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Spring Boot 服务容错 Sentinel 入门_第13张图片

此时,我们访问 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. 系统自适应限流

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

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

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

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

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

下面,我们来搭建一个系统自适应限流的 Spring Boot 示例。本着省时省力(努力偷懒)的原则,我们继续复用「2. 流量控制」小节的 lab-46-sentinel-demo 项目。

5.1 简单测试

重新启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

然后,点击 Sentinel 控制台的「系统规则」菜单,然后点击右上角「新增系统规则」按钮,弹出「新增系统保护规则」。填写降级规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第14张图片

  • 这里,为了测试方便,我们创建了一条 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 系统自适应限流而拒绝,返回 "请求过于频繁"

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Spring Boot 服务容错 Sentinel 入门_第15张图片

6. 黑白名单控制

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

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

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

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

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

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

6.1 SpringMvcConfiguration

在 SpringMvcConfiguration 配置类的 #addSentinelWebInterceptor(...) 方法中,增加自定义 RequestOriginParser,解析请求来源。代码如下:

// SpringMvcConfiguration#addSentinelWebInterceptor(...)

config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。

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

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

6.2 简单测试

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

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

之后,点击 GET:/demo/echo 资源所在列的「授权」按钮,弹出「新增授权规则」。填写授权规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第16张图片

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

③ 点击「新增」按钮,完成授权规则的添加。此时,会自动跳转到「授权规则」菜单。如下图所示:Spring Boot 服务容错 Sentinel 入门_第17张图片

④ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口时,就会有被 Sentinel 黑白名单控制而拒绝,最终返回 "请求过于频繁"

此时,点击 Sentinel 控制台的「实时监控」菜单,可以看到该接口被拒绝的统计。如下图所示:Spring Boot 服务容错 Sentinel 入门_第18张图片

我们来使用 Postman 来模拟一个来源为 test 的请求,如下图所示:Spring Boot 服务容错 Sentinel 入门_第19张图片

7. Sentinel 客户端 API

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

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

  • 在上述的示例中,我们使用的就是 Sentinel 提供的 sentinel-spring-webmvc-adapter 对 SpringMVC 的示例。
  • 更多 Sentinel 的适配框架介绍,可见《Sentinel 官方文档 —— 主流框架的适配》文章。

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

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

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 简单测试

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/entry_demo 接口。因为 Sentinel 客户端是懒加载的。

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

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 entry_demo 资源。如下图所示:Spring Boot 服务容错 Sentinel 入门_第20张图片

之后,我们给 entry_demo 资源添加一个每秒仅允许调用一次的流控规则。如下图所示:Spring Boot 服务容错 Sentinel 入门_第21张图片

③ 使用浏览器,访问 http://127.0.0.1:8080/demo/echo 接口两次,会有一次被 Sentinel 流量控制而拒绝,最终返回 "被拒绝"

8. 注解支持

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

在「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(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

特别地,若 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 简单测试

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/annotations_demo 接口。因为 Sentinel 客户端是懒加载的。

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

然后,点击 Sentinel 控制台的「簇点链路」菜单,可以看到 annotations_demo_resource 资源。如下图所示:Spring Boot 服务容错 Sentinel 入门_第22张图片

之后,我们给 annotations_demo_resource 资源添加一个每 60 秒的异常比例是 10% 的降级规则。如下图所示:Spring Boot 服务容错 Sentinel 入门_第23张图片

③ 使用浏览器,访问 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 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 作为数据源

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

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

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

10.1 引入依赖

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



    com.alibaba.csp
    sentinel-datasource-nacos
    1.7.1
  • 引入 sentinel-datasource-nacos 依赖,实现 Sentinel 对 Nacos 作为数据源的支持。

10.2 NacosDataSource

因为 Sentinel 暂时没有提供 Spring Boot Starter 项目,所以我们需要自己创建 NacosDataSource Bean 对象,实现接入 Nacos 作为数据源。下面,我们参考 spring-cloud-alibaba-sentinel-datasource 项目中关于 Nacos 的代码,在 SentinelConfiguration 配置类中,增加 NacosDataSource Bean 的创建:

@Bean
public NacosDataSource nacosDataSource(ObjectMapper objectMapper) {
    // <1> Nacos 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
    String serverAddress = "127.0.0.1:8848"; // Nacos 服务器地址
    String namespace = ""; // Nacos 命名空间
    String dataId = "demo-application-flow-rule"; // Nacos 配置集编号
//        String dataId = "example-sentinel"; // Nacos 配置集编号
    String group = "DEFAULT_GROUP"; // Nacos 配置分组

    // <2> 创建 NacosDataSource 对象
    Properties properties = new Properties();
    properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddress);
    properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
    NacosDataSource> nacosDataSource = new NacosDataSource<>(properties, group, dataId,
            new Converter>() { //  转换器,将读取的 Nacos 配置,转换成 FlowRule 数组
                @Override
                public List convert(String value) {
                    try {
                        return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

    // 注册到 FlowRuleManager 中
    FlowRuleManager.register2Property(nacosDataSource.getProperty());
    return nacosDataSource;
}
  • <1> 处,Nacos 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
  • <2> 处,创建 NacosDataSource 对象,实现 Sentinel 对 Nacos 作为数据源的支持。这里比较难理解的是  处创建的 Converter 类,它负责将从 Nacos 读取的配置,转换成 FlowRule(流控规则) 数组。
  • <3> 处,注册到FlowRuleManager(流控规则管理器) 中。

另外,如果胖友想要接入其它 Sentinel 规则,需要创建多个 NacosDataSource Bean 对象,并编写对应的 Converter 类。

还有,NacosDataSource 已经实现了 Nacos 配置监听器。也就是说,在应用启动的情况下,如果我们更改了 Nacos 中的配置,应用中的 Sentinel 规则也会立即刷新。

10.3 创建 Nacos 配置集

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

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

也因此,我门直接在 Nacos 中,创建一个配置集 demo-application-flow-rule,具体内容如下图:Spring Boot 服务容错 Sentinel 入门_第24张图片

  • 推荐配置集编号的命名规则为 ${applicationName}-${ruleType},例如我们这里是 demo-application-flow-rule,即 demo-application 应用的流控规则。

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

    [
        {
            "resource": "GET:/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 简单测试

启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 Nacos 数据源加载而来的。如下图所示:Spring Boot 服务容错 Sentinel 入门_第25张图片

③ 使用浏览器,快速访问 http://127.0.0.1:8080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"

11. 使用 Apollo 作为数据源

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

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

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

11.1 引入依赖

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



    com.alibaba.csp
    sentinel-datasource-apollo
    1.7.1
  • 引入 sentinel-datasource-apollo 依赖,实现 Sentinel 对 Apollo 作为数据源的支持。

11.2 ApolloDataSource

因为 Sentinel 暂时没有提供 Spring Boot Starter 项目,所以我们需要自己创建 ApolloDataSource Bean 对象,实现接入 Apollo 作为数据源。下面,我们参考 spring-cloud-alibaba-sentinel-datasource 项目中关于 Apollo 的代码,在 SentinelConfiguration 配置类中,增加 ApolloDataSource Bean 的创建:

@Value("${spring.application.name}")
private String applicationName;

@Bean
public ApolloDataSource apolloDataSource(ObjectMapper objectMapper) {
    // <1> Apollo 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
    String appId = applicationName; // Apollo 项目编号。一般情况下,推荐和 spring.application.name 保持一致
    String serverAddress = "http://localhost:8080"; // Apollo Meta 服务器地址
    String namespace = "application"; // Apollo 命名空间
    String flowRuleKey = "sentinel.flow-rule"; // Apollo 配置项的 KEY

    // <2> 创建 ApolloDataSource 对象
    System.setProperty("app.id", appId);
    System.setProperty("apollo.meta", serverAddress);
    ApolloDataSource> apolloDataSource = new ApolloDataSource<>(namespace, flowRuleKey, "",
            new Converter>() { //  转换器,将读取的 Apollo 配置,转换成 FlowRule 数组
                @Override
                public List convert(String value) {
                    try {
                        return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

    // <3> 注册到 FlowRuleManager 中
    FlowRuleManager.register2Property(apolloDataSource.getProperty());
    return apolloDataSource;
}
  • <1> 处,Apollo 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。注意,这里我们是创建了一个 Apollo 配置项 sentinel.flow-rule,存储 Sentinel 的流控规则。
  • <2> 处,创建 ApolloDataSource 对象,实现 Sentinel 对 Apollo 作为数据源的支持。这里比较难理解的是  处创建的 Converter 类,它负责将从 Apollo 读取的配置,转换成 FlowRule(流控规则) 数组。
  • <3> 处,注册到FlowRuleManager(流控规则管理器) 中。

另外,如果胖友想要接入其它 Sentinel 规则,需要创建多个 ApolloDataSource Bean 对象,并编写对应的 Converter 类。

还有,ApolloDataSource 已经实现了 Apollo 配置监听器。也就是说,在应用启动的情况下,如果我们更改了 Apollo 中的配置,应用中的 Sentinel 规则也会立即刷新。

11.3 配置文件

创建 application.yaml 配置文件,自定义 Spring Boot 应用端口,避免和本地的 Apollo Config Service 使用的 8080 端口冲突。配置如下:

spring:
  application:
    name: demo-application

server:
  port: 18080 # 避免和 Apollo 使用到的 8080 端口冲突

11.4 创建 Apollo 配置

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

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

也因此,我门直接在 Apollo 中,创建一个配置项 sentinel.flow-rule,具体内容如下图:Spring Boot 服务容错 Sentinel 入门_第26张图片

  • 推荐配置项的 Key 的命名规则为 sentinel.${ruleType},例如我们这里是 sentinel.flow-rule,即应用的流控规则。

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

[
    {
        "resource": "GET:/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 简单测试

启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:18080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 Apollo 数据源加载而来的。如下图所示:Spring Boot 服务容错 Sentinel 入门_第27张图片

③ 使用浏览器,快速访问 http://127.0.0.1:18080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"

12. 使用 File 作为数据源

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

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

下面,我们从「2. 流量控制」小节的 lab-46-sentinel-demo 项目,复制出 lab-46-sentinel-demo-file 项目,改造成接入 File 作为数据源。

12.1 引入依赖

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



    com.alibaba.csp
    sentinel-datasource-extension
    1.7.1
  • 引入 sentinel-datasource-extension 依赖,实现 Sentinel 对 File 作为数据源的支持。

12.2 FileRefreshableDataSource

FileRefreshableDataSource,支持从本地文件读取 Sentinel 规则。同时,通过定时读取实现刷新,默认间隔为 3 秒。

下面,我们在 SentinelConfiguration 配置类中,创建 FileRefreshableDataSource Bean 对象,实现对项目中的 flow-rule.json 配置文件。代码如下:

@Bean
public FileRefreshableDataSource> refreshableDataSource(ObjectMapper objectMapper) throws IOException {
    // File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
    ClassPathResource resource = new ClassPathResource("/flow-rule.json");

    // 创建 FileRefreshableDataSource 对象
    FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(resource.getFile(),
            new Converter>() { //  转换器,将读取的文本配置,转换成 FlowRule 数组
                @Override
                public List convert(String value) {
                    try {
                        return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

    // 注册到 FlowRuleManager 中
    FlowRuleManager.register2Property(refreshableDataSource.getProperty());
    return refreshableDataSource;
}

<1> 处,读取配置文件的地址为 classpath:/flow-rule.json。如下图所示:Spring Boot 服务容错 Sentinel 入门_第28张图片 

[
  {
    "resource": "GET:/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、匀速排队)

  • <2> 处,创建 FileRefreshableDataSource 对象,实现 Sentinel 对 File 作为数据源的支持。这里比较难理解的是  处创建的 Converter 类,它负责将从 File 读取的配置,转换成 FlowRule(流控规则) 数组。

  • <3> 处,注册到FlowRuleManager(流控规则管理器) 中。

12.3 简单测试

启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

点击 Sentinel 控制台的「流控规则」菜单,可以看到应用中已经有一条流控规则,是从 File 数据源加载而来的。如下图所示:Spring Boot 服务容错 Sentinel 入门_第29张图片

③ 使用浏览器,快速访问 http://127.0.0.1:8080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"

12.4 FileWritableDataSource

FileWritableDataSource,支持接收 Sentinel 控制的规则推送,写入配置到指定文件。

下面,我们在 SentinelConfiguration 配置类中,创建 FileWritableDataSource Bean 对象,实现接收 Sentinel 控制的规则推送,写入到 ${user.home}/sentinel/${project.name}/flow-rule.json 配置文件中。代码如下:

友情提示:请注释掉「12.2 FileRefreshableDataSource」小节中,创建 FileRefreshableDataSource Bean 的代码。因为在 Sentinel 客户端中,每个 Sentinel 每种规则管理器只允许注册一个 DataSource。

而本小节中,我们还是会注册一个 FileRefreshableDataSource 对象,因此会存在冲突。

@Bean
public FileWritableDataSource writableDataSource(ObjectMapper objectMapper) throws IOException {
    // <1> File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
    String directory = System.getProperty("user.home") + File.separator
            + "sentinel" + File.separator
            + System.getProperty("project.name");
    mkdirs(directory);
    String path = directory + File.separator + "flow-rule.json";
    creteFile(path);

    // <2> 创建 FileRefreshableDataSource 对象
    FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(path,
            new Converter>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组
                @Override
                public List convert(String value) {
                    try {
                        return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
    // 注册到 FlowRuleManager 中
    FlowRuleManager.register2Property(refreshableDataSource.getProperty());

    // <3> 创建 FileWritableDataSource 对象
    FileWritableDataSource> fileWritableDataSource = new FileWritableDataSource<>(path,
            new Converter, String>() {
                @Override
                public String convert(List source) {
                    try {
                        return objectMapper.writeValueAsString(source);
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

    // 注册到 WritableDataSourceRegistry 中
    WritableDataSourceRegistry.registerFlowDataSource(fileWritableDataSource);
    return fileWritableDataSource;
}

private void mkdirs(String path) {
    File file = new File(path);
    if (file.exists()) {
        return;
    }
    file.mkdirs();
}

private void creteFile(String path) throws IOException {
    File file = new File(path);
    if (file.exists()) {
        return;
    }
    file.createNewFile();
}
  • <1> 处,定义 File 配置,并保证 File 一定存在。
  • <2> 处,创建对 File 读取的 FileRefreshableDataSource 对象,并注册到 FlowRuleManager 中。
  • <3> 处,创建对 File 读取的 FileWritableDataSource 对象,并注册到 WritableDataSourceRegistry 中。

12.5 简单测试

重启启动 Spring Boot 应用。

① 使用浏览器,访问下 http://127.0.0.1:8080/demo/echo 接口。因为 Sentinel 客户端是懒加载的。

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

点击 Sentinel 控制台的「流控规则」菜单,给 GET:/demo/echo 资源新建一条流控规则,如下图所示:Spring Boot 服务容错 Sentinel 入门_第30张图片

新建完成后,查看 ${user.home}/sentinel/${project.name}/flow-rule.json 配置文件,会发现该流控规则已经存储进来。操作命令行如下:

$ cat /Users/yunai/sentinel/demo-application/flow-rule.json
[{"resource":"GET:/demo/echo","limitApp":"default","grade":1,"count":5.0,"strategy":0,"refResource":null,"controlBehavior":0,"warmUpPeriodSec":10,"maxQueueingTimeMs":500,"clusterMode":false,"clusterConfig":{"flowId":null,"thresholdType":0,"fallbackToLocalWhenFail":true,"strategy":0,"sampleCount":10,"windowIntervalMs":1000}}]
  • 有一点要注意,Sentinel 是按照应用实例创建规则,所以只会推送给一个应用实例。也就是说,如果一个应用我们启动了多个实例,需要配置多次,才能保证每个实例的配置文件,都持久化了该规则。

③ 使用浏览器,快速访问 http://127.0.0.1:8080/demo/echo 接口 6 次,最后 1 次会被 Sentinel 流量控制而拒绝,最终返回 "请求过于频繁"

13. 集群流控

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

  • 《Sentinel 官方文档 —— 集群流控》
  • 《Sentinel 实战 - 集群流控》
  • 《Sentinel 实战 - 集群流控环境搭建》

Sentinel 的东西真是多呀,所以胖友一定要多看看《Sentinel 官方文档》。本文的示例,大体覆盖到 Sentinel 文档所有的内容,遗漏的章节如下:

  • Roadmap
  • FAQ
  • 网关流控
  • 实时监控数据
  • Envoy 集群流量控制
  • 多语言生态
  • 开源贡献指南

Sentinel 支持的数据源比较多,艿艿暂时只写了三个主流的,剩余的胖友可以看看如下示例代码:

  • sentinel-demo-zookeeper-datasource
  • sentinel-demo-etcd-datasource
  • sentinel-datasource-redis 暂无示例。
  • sentinel-datasource-consul 暂无示例。
  • sentinel-datasource-spring-cloud-config 暂无示例。

 

你可能感兴趣的:(Sentinel,服务容错,Spring,Boot)