官网: https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

1. 介绍

image-20211220231628819

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

1)Sentinel核心组件

1:核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。

2:控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

2)Sentinel vs Hystrix

对比内容SentinelHystrix
隔离策略信号量隔离线程池隔离/信号量隔离
熔断降级策略基于响应时间或失败比率基于失败比率
实时指标实现滑动窗口滑动窗口(基于 RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
限流基于 QPS,支持基于调用关系的限流不支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

3)Sentinel基本概念

  • 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
  • 规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

2. Sentinel核心功能

2.1 流量控制

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

image-20211220231832455

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

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

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

2.2 熔断降级

1)什么是熔断降级

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。

image-20211220231924602

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

2)Sentinel熔断降级设计

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

Sentinel熔断降级设计:

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

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

3)系统自适应保护

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

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

3. Sentinel集成Gateway

由于目前网关大多数都是用SpringCloud Gateway,因此重点讲解Sentinel集成Gateway。

3.1 Sentinel对网关支持

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。

image-20211220232116419

Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:

  • GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  • ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

其中网关限流规则 GatewayFlowRule 的字段解释如下:

  • resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。

  • resourceMode:规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。

  • grade:限流指标维度,同限流规则的 grade 字段。

  • count:限流阈值

  • intervalSec:统计时间窗口,单位是秒,默认是 1 秒。

  • controlBehavior:流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。

  • burst:应对突发请求时额外允许的请求数目。

  • maxQueueingTimeoutMs:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。

  • paramItem:

    参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:

    • parseStrategy:从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
    • fieldName:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
    • pattern:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)
    • matchStrategy:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)

用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)。

3.2 集成Sentinel

我们如果想要让微服务网关集成Sentinel,需要引入依赖包,使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

依赖:

<!--Sentinel-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    <version>1.8.0</version>
</dependency>

创建相关配置类:

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 限流的异常处理器
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /***
     * 获取当前Route并按照当前Sentinel流量控制规则做处理
     * Sentinel路由处理核心过滤器
     * @return
     */
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /***
     * 初始化加载Api和规则
     */
    @PostConstruct
    public void doInit() {
        initCustomizedApis();
        initGatewayRules();
    }

    /***
     * Api定义
     */
    private void initCustomizedApis() {
        //定义集合存储的API组
        Set<ApiDefinition> definitions = new HashSet<>();
        //创建API,并配置
        ApiDefinition cartApi = new ApiDefinition("mall_cart_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/cart/list"));
                    add(new ApiPathPredicateItem().setPattern("/cart/*")
                            //参数值的匹配策略
                            // 精确匹配(PARAM_MATCH_STRATEGY_EXACT)
                            // 子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)
                            // 正则匹配(PARAM_MATCH_STRATEGY_REGEX)
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        //将创建好的API添加到集合中
        definitions.add(cartApi);
        //加载Api到sentinel中
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /***
     * 规则定义
     */
    private void initGatewayRules() {
        //网关限流规则集合
        Set<GatewayFlowRule> rules = new HashSet<>();

        //商品微服务规则配置
        //资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
        rules.add(new GatewayFlowRule("goods_route")
                //限流阈值
                .setCount(2)
                //应对突发请求时额外允许的请求数目。
                .setBurst(2)
                //统计时间窗口,单位是秒,默认是 1 秒。
                .setIntervalSec(1)
                //限流行为
                //CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
                //CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
                //匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
                .setMaxQueueingTimeoutMs(1000)
        );

        //API 【mall_cart_api】限流配置
        rules.add(new GatewayFlowRule("mall_cart_api")
                //限流阈值
                .setCount(3)
                //应对突发请求时额外允许的请求数目。
                .setBurst(3)
                //统计时间窗口,单位是秒,默认是 1 秒。
                .setIntervalSec(1)
        );
        //加载网关规则
        GatewayRuleManager.loadRules(rules);
    }
}

集成完毕。

3.3 配置API定义

1)API定义

正如前面所说,ApiDefinition用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

我们在配置类创建相关Api:

/***
 * Api定义
 */
private void initCustomizedApis() {
    //Api集合
    Set<ApiDefinition> definitions = new HashSet<ApiDefinition>();
    ApiDefinition cartApi = new ApiDefinition("mall_cart_api")
            .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                add(new ApiPathPredicateItem().setPattern("/cart/list"));
                add(new ApiPathPredicateItem().setPattern("/cart/*")
                        //参数值的匹配策略
                        // 精确匹配(PARAM_MATCH_STRATEGY_EXACT)
                        // 子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)
                        // 正则匹配(PARAM_MATCH_STRATEGY_REGEX)
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }});
    definitions.add(cartApi);
    //加载Api
    GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}

2)规则创建

GatewayFlowRule网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。

我们在配置类创建相关配置:

/***
 * 规则定义
 */
private void initGatewayRules() {
    //网关限流规则
    Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();

    //微服务规则配置
    //资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
    rules.add(new GatewayFlowRule("goods_route")
            //限流阈值
            .setCount(2)
            //应对突发请求时额外允许的请求数目。
            .setBurst(2)
            //统计时间窗口,单位是秒,默认是 1 秒。
            .setIntervalSec(1)
            //限流行为
            //CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
            //CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
            .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
            //匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
            .setMaxQueueingTimeoutMs(1000)
    );
    //API 【mall_cart_api】限流配置
    rules.add(new GatewayFlowRule("mall_cart_api")
            //限流阈值
            .setCount(3)
            //统计时间窗口,单位是秒,默认是 1 秒。
            .setIntervalSec(1)
    );
    //加载网关规则
    GatewayRuleManager.loadRules(rules);
}

3)配置Api加载

上面两个方法创建了API并且创建了配置,但并没有在程序启动加载,我们可以采用@PostConstruct注解实现加载调用,在配置类中中创建初始化方法:

/***
 * 初始化加载Api和规则
 */
@PostConstruct
public void doInit() {
    initCustomizedApis();
    initGatewayRules();
}

4. Sentinel控制台

Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。

4.1 Sentinel控制台安装

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。

Sentinel 控制台包含如下功能:

  • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
  • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
  • 规则管理和推送:统一管理推送规则。
  • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装,为了方便操作,我这里采用docker安装方式:

docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard

安装好了后,我们可以直接访问http://loclahost:8858访问控制台,默认用户名和密码都是 sentinel:

image-20211220232833267

登陆后:

image-20211220232908343

4.2 接入控制台

我们将Gateway微服务网关接入Sentinel控制台,首先引入依赖包:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

在核心配置文件中配置Sentinel服务地址

spring:
  cloud:
    sentinel:
      transport:
        # 服务本地端口
        port: 8719
        dashboard: 127.0.0.1:8858

这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互,比如限流规则拉取。

此时我们出发一些请求操作,再看Sentinel控制台会多一个服务监控:

image-20211220233005680

更多推荐