ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring Cloud Alibaba 服务容错 Sentinel 入门

2021-06-16 22:29:29  阅读:239  来源: 互联网

标签:数据源 Spring 规则 Alibaba sentinel demo Sentinel 控制台


原博文,点击这里

参考文章,点击这里

文章目录


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 次,才会被限流。
在这里插入图片描述

标签:数据源,Spring,规则,Alibaba,sentinel,demo,Sentinel,控制台
来源: https://blog.csdn.net/weixin_42030357/article/details/117968060

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有