乐趣区

关于java:阿里巴巴开源限流组件Sentinel初探

1 Sentinel 主页

https://github.com/alibaba/Se…

1.1 Sentinel 介绍

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

1)Sentinel 外围组件

1: 外围库(Java 客户端):不依赖任何框架 / 库,可能运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的反对。2: 控制台(Dashboard):控制台次要负责管理推送规定、监控、集群限流调配治理、机器发现等。

2)Sentinel vs Hystrix

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

3)Sentinel 基本概念

  • 资源
资源是 Sentinel 的要害概念。它能够是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它利用提供的服务,甚至能够是一段代码。只有通过 Sentinel API 定义的代码,就是资源,可能被 Sentinel 爱护起来。大部分状况下,能够应用办法签名,URL,甚至服务名称作为资源名来标示资源。
  • 规定
围绕资源的实时状态设定的规定,能够包含流量管制规定、熔断降级规定以及零碎爱护规定。所有规定能够动静实时调整。

1.2 Sentinel 外围性能

1.2.1 流量管制

流量管制在网络传输中是一个罕用的概念,它用于调整网络包的发送数据。然而,从零碎稳定性角度思考,在解决申请的速度上,也有十分多的考究。任意工夫到来的申请往往是随机不可控的,而零碎的解决能力是无限的。咱们须要依据零碎的解决能力对流量进行管制。Sentinel 作为一个调配器,能够依据须要把随机的申请调整成适合的形态,如下图所示:

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

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

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

1.2.2 熔断降级

1)什么是熔断降级

除了流量管制以外,及时对调用链路中的不稳固因素进行熔断也是 Sentinel 的使命之一。因为调用关系的复杂性,如果调用链路中的某个资源呈现了不稳固,可能会导致申请产生沉积,进而导致级联谬误。

Sentinel 和 Hystrix 的准则是统一的: 当检测到调用链路中某个资源呈现不稳固的体现,例如申请响应工夫长或异样比例升高的时候,则对这个资源的调用进行限度,让申请疾速失败,防止影响到其它的资源而导致级联故障。

2)Sentinel 熔断降级设计

Hystrix 通过 线程池隔离 的形式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的益处是资源和资源之间做到了最彻底的隔离。毛病是除了减少了线程切换的老本(过多的线程池导致线程数目过多),还须要事后给各个资源做线程池大小的调配。

Sentinel 熔断降级设计:

并发线程数限度: 和资源池隔离的办法不同,Sentinel 通过限度资源并发线程的数量,来缩小不稳固资源对其它资源的影响。这样岂但没有线程切换的损耗,也不须要您事后调配线程池的大小。当某个资源呈现不稳固的状况下,例如响应工夫变长,对资源的间接影响就是会造成线程数的逐渐沉积。当线程数在特定资源上沉积到肯定的数量之后,对该资源的新申请就会被回绝。沉积的线程实现工作后才开始持续接管申请。响应工夫降级: 除了对并发线程数进行管制以外,Sentinel 还能够通过响应工夫来疾速降级不稳固的资源。当依赖的资源呈现响应工夫过长后,所有对该资源的拜访都会被间接回绝,直到过了指定的工夫窗口之后才从新复原。

3)零碎自适应爱护

Sentinel 同时提供零碎维度的自适应爱护能力。避免雪崩,是零碎防护中重要的一环。当零碎负载较高的时候,如果还继续让申请进入,可能会导致系统解体,无奈响应。在集群环境下,网络负载平衡会把本应这台机器承载的流量转发到其它的机器下来。如果这个时候其它的机器也处在一个边缘状态的时候,这个减少的流量就会导致这台机器也解体,最初导致整个集群不可用。

针对这个状况,Sentinel 提供了对应的爱护机制,让零碎的入口流量和零碎的负载达到一个均衡,保证系统在能力范畴之内解决最多的申请。

Sentinel 集成 Gateway

咱们的我的项目流量入口是SpringCloud Gateway,因而咱们重点解说 Sentinel 集成Gateway

3.1 Sentinel 对网关反对

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

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

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,能够提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId

    spring: 
       gateway:
    #路由配置
    routes:
      #惟一标识符
          - id: hailtaxi-driver
            uri: lb://hailtaxi-driver
            #路由断言
            predicates:
              - Path=/driver/**
          #惟一标识符
          - id: hailtaxi-order
            uri: lb://hailtaxi-order
            #路由断言
            predicates:
              - Path=/order/**

    主动将每个路由标识为资源,

  • 自定义 API 维度:用户能够利用 Sentinel 提供的 API 来自定义一些 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 GateWay 集成 Sentinel

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

1、首先在 hailtaxi-gateway 中引入如下依赖:

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

2、实例引入:创立配置类com.itheima.config.GatewayConfiguration:

package com.itheima.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@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);
    }

    /***
     * Sentinel 路由解决外围过滤器
     * @return
     */
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();
    }



    @PostConstruct
    public void doInit() {
        // 自定义 api 分组
        initCustomizedApis();
        // 初始化网关流控规定
        initGatewayRules();}

    private void initCustomizedApis() {Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("customer_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{add(new ApiPathPredicateItem().setPattern("/order/**")
                            /**
                             * 匹配策略:* URL_MATCH_STRATEGY_EXACT:url 准确匹配
                             * URL_MATCH_STRATEGY_PREFIX:url 前缀匹配
                             * URL_MATCH_STRATEGY_REGEX:url 正则匹配
                             */
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api1);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {Set<GatewayFlowRule> rules = new HashSet<>();

        rules.add(new GatewayFlowRule("hailtaxi-driver") // 资源名称,能够是网关中的 routeid 或者用户自定义的 API 分组名称
                .setCount(2) // 限流阈值
                .setIntervalSec(10) // 统计工夫窗口默认 1s
                .setGrade(RuleConstant.FLOW_GRADE_QPS) // 限流模式
                /**
                 * 限风行为:
                 * CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
                 * CONTROL_BEHAVIOR_DEFAULT 疾速失败(默认)
                 * CONTROL_BEHAVIOR_WARM_UP:* CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:*/
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
                // 匀速排队模式下的最长排队工夫,单位是毫秒,仅在匀速排队模式下失效
                .setMaxQueueingTimeoutMs(1000)
                /**
                 * 热点参数限流配置
                 * 若不设置, 该网关规定将会被转换成一般流控规定;否则会转换成热点规定
                 */
                .setParamItem(new GatewayParamFlowItem()
                        /**
                         * 从申请中提取参数的策略:
                         * PARAM_PARSE_STRATEGY_CLIENT_IP
                         * PARAM_PARSE_STRATEGY_HOST
                         * PARAM_PARSE_STRATEGY_HEADER
                         * PARAM_PARSE_STRATEGY_URL_PARAM
                         */
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
                        /**
                         * 若提取策略抉择 Header 模式或 URL 参数模式,* 则须要指定对应的 header 名称或 URL 参数名称。*/
                        .setFieldName("token")
                        /**
                         * 参数的匹配策略:* PARAM_MATCH_STRATEGY_EXACT
                         * PARAM_MATCH_STRATEGY_PREFIX
                         * PARAM_MATCH_STRATEGY_REGEX
                         * PARAM_MATCH_STRATEGY_CONTAINS
                         */
                        .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
                        // 参数值的匹配模式,只有匹配该模式的申请属性值会纳入统计和流控
                        .setPattern("123456") // token=123456 10s 内 qps 达到 2 次会被限流
                )
        );

        rules.add(new GatewayFlowRule("customer_api")
                /**
                 * 规定是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)* 还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。*/
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(2)
                .setIntervalSec(1)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
        );
        GatewayRuleManager.loadRules(rules);
    }


}

此时集成就实现了。

3、启动 hailtaxi-gatewayhailtaxi-drvierhailtaxi-order 测试:

应用 postman 测试,

申请:http://localhost:8001/driver/info/1 10 秒内申请超过 2 次会被限流

申请:localhost:8001/order 1 秒内 qps 达到 2 次会被限流

本文由传智教育博学谷 – 狂野架构师教研团队公布
转载请注明出处!

退出移动版