• Sentinel微服务限流、熔断、降级基本用法(二)


    概述

    Sentinel Dashboard的部署

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

    • 在码云上下载源码:https://gitee.com/mirrors/Sentinel/tags
      • 直接现在源码通过mvn clean package自己构建。
        在这里插入图片描述
      • 启动jar包
        使用如下命令启动编译后的控制台:
    java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
    
    • 1

    其中,启动参数的含义如下:

    • -Dserver.port:指定Sentinel控制台的访问端口,默认是8080
    • -Dcsp.sentinel.dashboard.server:指定Sentinel Dashboard控制台的IP地址和端口,这里进行设置的目的是把自己的限流数据暴露到监控平台。
    • -Dproject.name:设置项目的名称。
      从Sentinel 1.6.0开始,Sentinel控制台引入基本的登录功能,默认用户名和密码都是sentinel。

    登录Sentinel控制台

    在这里插入图片描述

    Sentinel Dashboard触发

    需要微服务接入Dashboard,同时需要访问微服务的任意端点(接口)才能触发监控,在Dashboard上看到微服务信息;如果只是接入Dashboard没有触发任意端点是无法在Dashboard上看到微服务信息的。

    Sentinel的基本使用

    使用Sentinel的核心库来实现限流,主要分以下几个步骤。

    • 定义资源。
    • 定义限流规则。
    • 检验规则是否生效。
      所谓的资源,就是需要通过限流包含的基本元素,比如一个方法。有了需要保护的资源之后,就可以针对该资源设置流量控制规则了。
      下面先通过一个简单的案例来演示一下Sentinel的基本使用方法,对Sentinel有一个基本的认识。

    Sentinel实现限流

    1. 首先,引入Sentinel的核心库。
            <dependency>
                <groupId>com.alibaba.cspgroupId>
                <artifactId>sentinel-coreartifactId>
                <version>1.7.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 定义资源
        private static void doSomething() {
            try (Entry entry = SphU.entry("doSomething")) {
                // 业务逻辑处理
                System.out.println("hello world " + System.currentTimeMillis());
            } catch (BlockException e) {
                // 处理流控的逻辑
                System.out.println("block exception, trigger traffic limit");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在doSomething方法中,通过使用Sentinel中的SphU.entry(“doSomething”)定义一个资源来实现流控的逻辑,它表示当请求进入doSomething方法时,需要进行限流判断。如果抛出BlockException异常,则表示触发了限流。

    1. 定义限流规则
        private static void initFlowRules() {
            List<FlowRule> rules = new ArrayList<>();
            FlowRule rule = new FlowRule();
            rule.setResource("doSomething");
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            rule.setCount(5);
            rules.add(rule);
            FlowRuleManager.loadRules(rules);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    针对资源doSomething,通过initFlowRules设置限流规则,其中参数的含义如下。

    • Grade:限流阈值类型,QPS(1)或并发线程模式(0)。
    • count:限流阈值。
    • resource:设置需要保护的资源。这个资源的名称必须和SphU.entry中使用的名称保持一致。
      上述代码的意思是,针对doSomething方法,每秒最多允许5个请求,也就是QPS为5。
    1. 测试限流
        public static void main(String[] args) {
            initFlowRules();
            while(true){
                doSomething();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行了main方法之后,可以在${USER_HOME}\logs\csp${包名-类名}-metrics.logs.date文件中看到如下日志:

    1660630594000|2022-08-16 14:16:34|doSomething|5|8152|5|0|0|0|0|0
    1660630595000|2022-08-16 14:16:35|doSomething|5|8964|5|0|0|0|0|0
    1660630596000|2022-08-16 14:16:36|doSomething|5|8956|5|0|0|0|0|0
    
    • 1
    • 2
    • 3

    上述日志中对应字段的具体含义是:

    timestamp|yyyy-MM-dd HH:mm:ss|resource|passQps|blockQps|successQps|exceptionQps|rt|occupiedPassQps|concurrency|classification
    
    • 1

    passQps:代表通过的请求。
    blockQps:代表被阻止的请求。
    successQps:代表成功执行完成的请求个数。
    exceptionQps:代表用户自定义的异常。
    rt:代表平均响应时长。
    occupiedPassQps:代表优先通过的请求。
    concurrency:代表并发量。
    classification:代表资源类型。
    从日志可以看出,这个程序喵喵文档输出(doSomething) 5次,和规则中预先设定的阈值时一样的,而被拒绝的请求每秒最高达8万多次。

    资源的定义方式

    SphU.entry方式定义资源

    在上面,我们通过抛出异常的方式来定义了一个资源,也就是当资源被限流之后,会抛出一个BlockException异常。这时我们需要捕获该异常进行限流后的逻辑处理。

      try (Entry entry = SphU.entry("resourceName")) {
                // 被保护的业务逻辑
                System.out.println("hello world " + System.currentTimeMillis());
            } catch (BlockException e) {
                // 被限流的逻辑
                System.out.println("block exception, trigger traffic limit");
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中,resourceName可以定义方法名称、接口名称或者其他的唯一标识。

    SphO.entry方式定义资源

        private static void doSomething1() {
            if (SphO.entry("resourceName")) {
                try {
                    // 被保护的业务逻辑
                } finally {
                    SphO.exit();
                }
            } else {
                // 资源访问被现在
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这种方式中,需要注意资源使用完之后要调用SphO.exit(),否则会导致调用链记录异常,抛出ErrorEntryFreeException异常。

    @SentinelResource方式定义资源

        @SentinelResource(value = "resourceName", entryType = EntryType.OUT, blockHandler = "exceptionHandler", fallback = "helloFallback")
        public String test() {
            double random = Math.random();
            System.out.println(random);
            System.out.println(1 / 0);
            return "success";
        }
        public String exceptionHandler(BlockException ex) {
            System.out.println("exception");
            return "exception";
        }
    
        public String helloFallback() {
            return "hello fall back";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    需要注意的是,blockHandler所配置的值exceptionHandler会在触发限流后调用,这个方法的定义必须和原始方法test的返回值、参数保持一致,而且需要增加BlockException参数。
    Sentinel资源的定义还有更多的方式,这里就不再一一强调了,感兴趣的小伙伴可以去官网看看。

    Sentinel资源保护规则

    Sentinel支持多种保护规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则、热点参数规则。
    限流规则在前面的案例中简单使用过,先通过FlowRule来定义限流规则,然后通过FlowRuleManager.loadRules来加载规则列表。完整的限流规则设置代码如下:

        public void init() {
            List<FlowRule> rules = new ArrayList<>();
            FlowRule rule = new FlowRule();
            rule.setCount(20);
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            rule.setLimitApp("default");
            rule.setStrategy(RuleConstant.STRATEGY_CHAIN);
            rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
            rule.setClusterMode(false);
            rules.add(rule);
            FlowRuleManager.loadRules(rules);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其中,FlowRule部分属性的含义说明如下:

    • limitApp:是否需要针对调用来源进行限流,默认是default,即不区分调用来源。
    • strategy:调用关系限流策略一一直接、链路、关联。
    • controlBehavior:流控行为,包括直接拒绝、排队等待、慢启动模式,默认是直接拒绝。
    • clusterMode:是否是集群限流,默认为否。
      下面基于这几个参数的含义做一个详细分析。

    grade属性(流控统计类型)

    Sentinel流量控制统计有两种类型,通过grade属性来控制:

    • 并发线程数(FLOW_GRADE_THREAD).
    • QPS(FLOW_GRADE_QPS).

    并发线程数

    并发线程数限流用来保护线程不被耗尽。比如,A服务调用B服务,而B服务因为某种原因导致服务不稳定或者响应延迟,那么对于A服务来说,它的吞吐量会下降,也意味着占用更多的线程(线程阻塞之后一直未释放),极端情况下会造成线程池耗尽。
    针对这种问题,一个常见的解决方案是通过不同业务逻辑使用不同的线程池来隔离业务自身的资源争抢问题,但是这个方案同样会造成线程数量过多带来的上下文切换问题。
    Sentinel并发线程数限流就是统计当前请求的上下文线程数量,如果超出阈值,新的请求就会被拒绝。

    QPS

    QPS(Queries Per Second)表示每秒的查询数,也就是一台服务器每秒能够响应的查询次数。当QPS达到限流的阈值时,就会触发限流策略。

    流控效果(QPS流量控制行为)

    当QPS超过阈值时,就会触发流量控制行为,这种行为是通过controlBehavior来设置的,它包含:

    • 直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
    • Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP),预热模式;
    • 匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
    • 热启动+匀速排队(RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER);

    直接拒绝

    直接拒绝是默认的流量控制方式,也就是请求流量超出阈值时,直接抛出一个FlowException。

    Warm Up

    Warm Up是一种热预热模式。当流量突然增大时,也就意味着系统从空闲状态突然切换到繁忙状态,有可能瞬间把系统压垮。当我们希望请求处理的数量逐步递增,并在一个预期时间之后到达允许处理请求的最大值时,Warm Up就可以达到这个目的。
    在这里插入图片描述
    如图所示,当前系统所能够处理的最大并发数是480,首先,在最下面标记的位置,系统一直处于空闲状态,接着请求量突然直线升高。这个时候系统并不是直接将QPS拉到最大值,而是在一定时间内逐步增加阈值,而中间这段时间就是一个系统逐步预热的过程。

    匀速排队

    匀速排队的方式会严格控制请求通过的间隔时间,也就是让请求以均匀的速度通过,其实相当于前面讲的漏桶限流算法。
    在这里插入图片描述
    如图所示,当QPS=2时,意味着每隔500ms才允许通过下一个请求,这种方式的好处是可以处理间隔性突发流量。

    流控模式

    调用关系包括调用方和被调用方,一个方法又可能会调用其他方法,形成一个调用链。所谓的调用关系流量策略,就是根据不同的调用维度来触发流量控制。

    • 根据调用方限流。
    • 根据调用链路入口限流。
    • 具有关系的资源流量控制(关联流量控制)。

    调用方限流

    所谓调用方限流,就是根据请求来源进行流量控制,我们需要设置limitApp属性来设置来源信息,它有三个选项。

    • default:表示不区分调用者,也就是任何访问调用者的请求都会进行限流统计。
    • {some_origin_name}:设置特定的调用者,只有来自这个调用者的请求才会进行流量统计和控制。
    • other:表示针对除{some_origin_name}外的其他调用者进行流量控制。
      由于同一个资源可以配置多条规则,如果多个规则设置的limitApp不一样,那么规则的生效顺序为:{some_origin_name}->other->default。

    链路(根据调用链路入口限流)

    一个被限流保护的方法,可能来自不同的调用链路。比如针对资源nodeA,入口Entrance1和入口Entrance2都调用了资源nodeA,那么Sentinel允许只根据某个入口来进行流量统计。比如我们针对nodeA资源,设置针对Entrance1入口的调用才会统计请求次数。它在一定程度上有点类似于调用方限流。
    在这里插入图片描述
    链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。
    例如有两条请求链路:

    • /test1 -> /common
    • /test2 -> /common
      如果只希望统计从/test2进入到/common的请求,则可以这样配置:
      在这里插入图片描述

    注意事项

    • Sentinel默认只标记Controller中的方法为资源,如果要标记非Controller其他方法,需要利用@SentinelResource注解,示例:
    @SentinelResource("goods")
    public void queryGoods() {
    	System.err.println("查询商品");
    }
    
    • 1
    • 2
    • 3
    • 4
    • Sentinel默认会将Controller方法整合,导致链路模式流控失效,需要修改application.properties,添加配置:
    # 关闭context整合
    spring.cloud.sentinel.web-contenxt-unify: false
    
    • 1
    • 2

    关联流量控制

    当两个资源之间存在依赖关系或者资源争抢时,我们就说这两个资源存在关联。这两个存在依赖关系的资源在执行时可能会因为某一个资源执行操作过于频繁而影响另外一个资源的执行效率,所以关联流量控制(流控)就是限制其中一个资源的执行流量。

    • 关联模式:统计与当前资源相关的另一个资源,另一个资源触发阈值是,对当前资源限流。
    • 使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务要求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
      在这里插入图片描述
      统计关联资源/write资源访问量,当访问量触发阈值时,就会对/read资源限流,避免影响/write资源。

    小结

    满足下面条件可以使用关联模式

    • 两个有竞争关系的资源。
    • 一个优先级较高,一个优先级较低。

    Sentinel实现服务熔断

    Sentinel实现服务熔断操作的配置和限流类似,不同之处在于限流采用的是FlowRule,而熔断中采用的是DegradeRule,配置代码如下:

        public void initDegradeRule(){
            List<DegradeRule> rules = new ArrayList<>();
            DegradeRule rule = new DegradeRule();
            rule.setResource("KEY");
            rule.setCount(10);
            rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
            rule.setTimeWindow(10);
            rule.setMinRequestAmount(5);
            rule.setRtSlowRequestAmount(5);
            rules.add(rule);
            DegradeRuleManager.loadRules(rules);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其中,有几个属性说明如下:

    • grade:熔断策略,支持秒级RT,秒级异常比例、分钟级异常数。默认是秒级RT。
    • timeWindow:熔断降级的时间窗口,单位为s。也就是触发熔断降级之后熔断多长时间。
    • rtSlowRequestAmout:在RT模式下,1s内持续多少个请求的平均RT超出阈值后触发熔断,默认值是5。
    • minRequestAmount:触发的异常熔断最小请求数,请求数小于该值时即使异常比例超出阈值也不会触发熔断,默认值是5。

    熔断策略

    Sentinel提供三种熔断策略,对于不同策略,参数的含义也不相同。

    平均响应时间(RuleConstant.DEGRADE_GRADE_RT)

    如果1s内持续进入5个请求,对应的平均响应时间都超过了阈值(count:单位ms),那么在接下来的时间窗口(timeWindow,单位s)内,对这个方法的调用都会自动熔断,抛出DegradeException。

    Sentinel默认统计的RT上限时4900ms,如果超出此阈值都会算作4900ms,如果需要修改,则通过启动参数 -Dcsp.sentinel.statistic.max.rt=xxx来配置

    异常比例(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)

    如果每秒资源数>=minRequestAmount(默认5),并且每秒的异常总数占通过量的比例超过阈值count(count的取值范围是[0.0,1.0]),代表0%~100%),则资源将进入降级状态。同样,在接下来的timeWindow之内,对这个方法的调用都会自动触发熔断。

    异常数(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)

    当资源最近一分钟的异常数目超过阈值之后,会触发熔断。需要注意的是,如果timeWindow小于60s,则结束熔断状态后仍然可能进入熔断状态。

  • 相关阅读:
    HarmonyOS(29)onMeasureSize和PlaceChildren (View的测量和布局)
    Flask Web——Jinjia2模板的使用
    SAP UI5 应用开发教程之一百零四 - SAP UI5 表格控件的支持复选(Multi-Select)以及如何用代码一次选中多个表格行项目
    记录 ubuntu 修改用户/root 密码步骤
    ES选举:Elasticsearch中Master选举完全解读
    汽车控制器软件正向开发
    Java 8 之 Optional
    [附源码]java毕业设计大学生日常行为评价管理系统
    Shiro - Shiro简介;Shiro与Spring Security区别;Spring Boot集成Shiro
    Android Fragment详解(一)——生命周期
  • 原文地址:https://blog.csdn.net/tianzhonghaoqing/article/details/126362522