欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud Alibaba Sentinel 流量管理

程序员文章站 2022-07-15 09:49:43
...

Spring Cloud Alibaba Sentinel 流量管理

Hi,我是空夜!

本节示例代码在 https://github.com/laolunsi/spring-boot-examples


首先下载 sentinel jar包:https://github.com/alibaba/Sentinel/releases

java -jar sentinel-xx.jar 运行,打开浏览器,输入默认地址:http://localhost:8080

Spring Cloud Alibaba Sentinel 流量管理

参考:如何使用 Sentinel?—— 官方文档

sentinel 进行流量控制有以下流程:

  • 定义资源
  • 定义规则
  • 检验规则是否生效

概念

资源,是 sentinel 的核心概念之一,可以简单的理解为一段代码。

sentinel 对主流的框架都提供了适配,下面以 Spring Cloud 为例,记录在 Spring Cloud 微服务架构中如何使用 sentinel 进行流量控制。

资源

分为以下几种方式:

  • 主流框架的默认配置
  • 抛出异常方式定义资源
  • 返回布尔值方式定义资源
  • 注解方式定义资源
  • 异步调用支持

注解方式定义资源主要用于接口上,对接口进行流量控制,使用的是 @ResourceSentinel 注解。该注解提供了可选的异常处理和 fallback 配置项。参考:Sentinel 注解支持

源码:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
    // 资源名称,不能为空
    String value() default "";

    EntryType entryType() default EntryType.OUT;

    int resourceType() default 0;

    // 可选项,处理 BlockException 的函数名称,默认在本类中;如果想要指定其他类中的函数,需要使用 blockHandlerClass 属性
    String blockHandler() default "";

    Class<?>[] blockHandlerClass() default {};

    // 可选项,fallback 函数名称,用于在抛出异常时提供 fallback 处理逻辑。可以针对所有类型的异常。配合 fallbackClass 属性可以指定其他类中的函数
    String fallback() default "";

    String defaultFallback() default "";

    Class<?>[] fallbackClass() default {};

    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

    // 忽略异常,即 fallback 和 blockHandler 函数不起作用的异常
    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

注意:

  • blockHandler 和 fallback 指定的函数默认在本类中,该函数的返回值类型和参数顺序需与原方法保持一致。blockHandler 对应的函数可以在最后额外加一个 BlockException 类型的参数,fallback 对应的函数可以再最后额外加一个 Throwable 类型的参数
  • 使用对应的 blockHandlerClass 和 fallbackClass 可以指定对应函数所在的类,而不是默认的当前类。注意,此时,指定的函数需要是 static 的,否则无法解析。

规则

原理是监控应用流量的 QPS 或并发线程数等指标,设置阈值,避免服务被瞬时的高峰流量冲垮。目的是保障高峰流量时应用的高可用性。

目的是防止单个服务不可用导致的雪崩现象的发生,同样是保障应用高可用性的方法之一。

针对的是调用链路中不稳定的资源。

系统保护规则从应用级别(而不是资源维度)的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

根据调用来源判断请求是否允许通过。

看作一种特殊的流量控制规则,仅对包含热点参数的资源调用生效。它根据传入参数中的热点参数,与配置的限流阈值、模式,对包含热点参数的资源调用进行限流。

sentinel 还提供了相关 API 用于定制自己的规则策略。

上面默认的规则分类,可以通过代码定义,也可以使用 dashboard 定义。

定义规则可以通过 dashboard,也可以使用代码创建。如果想要持久化存储规则,可以利用 Nacos 等数据源。这一点在后面会讲。


Spring Cloud 整合 Sentinel

参考:spring-cloud-alibaba-sentinel

创建一个 demo 服务,引入 spring-cloud 中的 sentinel 依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>

配置:

server:
  port: 8203
spring:
  application:
    name: demo
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8081

可以配置流控、降级、热点、授权多种规则:
Spring Cloud Alibaba Sentinel 流量管理

注解支持使用参考:注解支持 —— 官方文档

默认情况下是以 url 作为链路地址以及资源名的,我们可以在代码中用 @SentinelResource 注解来主动注明资源的名称、阻塞处理方法等。比如:

@RestController
@RequestMapping(value = "test")
@Validated
public class TestAction {

    private static Logger logger = LoggerFactory.getLogger(TestAction.class);

    @SentinelResource(value = "helloTest", blockHandler = "handleEx")
    @GetMapping(value = "hello")
    public String hello(@RequestParam("msg") @NotBlank String msg) {
        logger.info("hellow, " + msg);
        return "hello, " + msg;
    }

    @GetMapping(value = "send")
    @SentinelResource(value = "sendTest", blockHandler = "handleException", blockHandlerClass = BlockHandlerConfig.class)
    public String send(@NotBlank(message = "{required}") String email, @Email(message = "{invalid}") String msg) {
        return "发消息给:" + email + ",消息内容:" + msg;
    }

    public String handleEx(String msg, BlockException ex) {
        System.out.println("系统错误:msg=" + msg + ", ex: " + ex);
        return "流量限制,请稍后重试";
    }
}

这里的 helloTest 使用了本类中的 handleEx 方法作为阻塞处理方法。而 sendTest 用 BlockHandlerConfig 类中的 handleException 方法:

public class BlockHandlerConfig {

    public static String handleException(String email, String msg, BlockException exception) {
        return "444, " + exception.getClass().getCanonicalName() + "\t 服务不可用";
    }
    
}

需要注意的是,blockHandle 对应方法的参数必须与 资源 的参数保持一致,否则规则不会生效,且会抛出异常。

这里给 helloTest 这个 resource 配置一个简单的流控:

Spring Cloud Alibaba Sentinel 流量管理

测试:

快速请求 helloTest 对应的接口,发现成功请求与限流响应交错出现了。这表明我们的限流规则和 blockHandler 生效了。
Spring Cloud Alibaba Sentinel 流量管理


规则持久化

需要注意的是,如果服务重启了,那么这些规则配置就会被丢失。其中一个解决办法是利用 Nacos 做配置中心,先将规则定义保存在 Nacos 中。

sentinel 提供了 file/nacos/zp/apollo 等规则扩展存储方式。具体参考官方文档:动态规则扩展

一个服务若要使用 Nacos 中的配置作为 sentinel 规则,除了 Nacos 的依赖外,还需要引入如下依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>

                <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.8.0</version>
        </dependency>

        <!-- 为解决 Caused by: java.lang.ClassNotFoundException: com.alibaba.csp.sentinel.log.CommandCenterLog 引入 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.0</version>
        </dependency>

配置:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: sentinel-config
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

nacos 中新建一个 sentinel-config 配置文件,类型是 json,内容比如:

[
    {
         "resource": "helloTest",
         "limitApp": "default",
         "grade":   1,
         "count":   3,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false    
    }
]

Spring Cloud Alibaba Sentinel 流量管理

启动后可以看到 sentinel 中出现了这个规则。在 nacos 中修改该规则,发现 sentinel 那边同步修改了(反之则不行)

Spring Cloud Alibaba Sentinel 流量管理


FAQ —— sentinel 部署路径的问题

之前部署 sentinel 的时候,出现过这样一个问题:我配置了一个域名给微服务平台使用,其中的每一个模板,如 gateway, zipkin,sentinel,都是用 nginx 配置的 xxx.com/gateway/xxx 这样的请求格式来访问的。

打开 xxx.com/sentinel, 是可以看到 dashboard 和每个服务的,但是点开服务,发现监控数据没有了,簇点链路中每个地址的 QPS 都是0,点击新增流控规则,控制台报了 404 异常。

在官方仓库提了这个 issue,有位j叫 jasonjoo2010 的老哥给我解答了一下:

目前dashboard的静态部分,并不支持任意子目录部署的方式,建议在不改动的情况下,使用独立域名部署。

后来我新增了一个二级域名来专门访问 sentinel,就可以了。

关于这个问题的详细描述在 sentinel 官方仓库中:https://github.com/alibaba/Sentinel/issues/1804


参考:


最近在系统地学习 Redis、RabbitMQ、ES 等技术的知识,着重关注原理、底层、并发等问题,关于相关技术分享后续会逐渐发布出来。欢迎关注公众号:猿生物语(ID:JavaApes