Springcloud基础知识(14)- Spring Cloud Alibaba Sentinel (二) | 流量控制、熔断降级

 

1. 流量控制

    任何系统处理请求的能力都是有限的,但任意时间内到达系统的请求量往往是随机且不可控的,如果在某一个瞬时时刻请求量急剧增,那么系统就很有可能被瞬时的流量高峰冲垮。

    为了避免此类情况发生,都需要根据系统的处理能力对请求流量进行控制,这就是常说的“流量控制”,简称“流控”。

    Sentinel 作为一种轻量级高可用流量控制组件,流量控制是它最主要的工作之一。

    我们可以针对资源定义流控规则,Sentinel 会根据这些规则对流量相关的各项指标进行监控。当这些指标当达到或超过流控规则规定的阈值时,Sentinel 会对请求的流量进行限制(即“限流”),以避免系统被瞬时的流量高峰冲垮,保障系统的高可用性。

    一条流量规则主要由下表中的属性组成,我们可以通过组合这些属性来实现不同的限流效果。

属性 描述
资源名 流控规则的作用对象。
阈值 流控的阈值。
阈值类型 流控阈值的类型,包括 QPS 或并发线程数。默认值:QPS
针对来源 流控针对的调用来源。默认值:default,表示不区分调用来源
流控模式 调用关系限流策略,包括直接、链路和关联。默认值:直接
流控效果 流控效果(直接拒绝、Warm Up、匀速排队),不支持按调用关系限流。默认值:直接拒绝


        注:QPS 表示并发请求数,换句话说就是,每秒钟最多通过的请求数。

    同一个资源可以创建多条流控规则,Sentinel 会遍历这些规则,直到有规则触发限流或者所有规则遍历完毕为止。

    Sentinel 触发限流时,资源会抛出 BlockException 异常,此时我们可以捕捉 BlockException 来自定义被限流之后的处理逻辑。

    注:这里我们主要讲解 Sentinel 流控规则的定义与使用,至于详细的流控规则配置,请参考 Sentinel 官方流控文档(https://sentinelguard.io/zh-cn/docs/flow-control.html)。


    1) 通过 Sentinel 控制台定义流控规则

        本文在 “ Springcloud基础知识(13)- Spring Cloud Alibaba Sentinel (一) | Sentinel 控制台、Sentinel 整合、定义资源 ” 里 SpringcloudDemo04 项目基础上,修改 ServiceSentinel 子模块的代码,并在 Sentinel 控制台添加配置,演示流控规则。

        (1) 修改 src/main/java/com/example/controller/IndexController.java 文件

 1             package com.example.controller;
 2 
 3             import com.alibaba.csp.sentinel.annotation.SentinelResource;
 4             import org.springframework.beans.factory.annotation.Value;
 5             import org.springframework.web.bind.annotation.GetMapping;
 6             import org.springframework.web.bind.annotation.PathVariable;
 7             import org.springframework.web.bind.annotation.RestController;
 8 
 9             import com.alibaba.csp.sentinel.Entry;
10             import com.alibaba.csp.sentinel.SphU;
11             import com.alibaba.csp.sentinel.SphO;
12             import com.alibaba.csp.sentinel.slots.block.BlockException;
13 
14             @RestController
15             public class IndexController {
16                 @Value("${server.port}")
17                 private String serverPort;
18                 @Value("${spring.application.name}")
19                 private String appName;
20 
21                 ...
22 
23                 @GetMapping(value = "/service/anno")
24                 @SentinelResource(value = "annoRes", blockHandler = "blockHandlerAnnoRes")
25                 public String annoRes() {
26                     String str = "<h2>Service Sentinel - Annotation Resource</h2>";
27                     str += "<p>Service Name: " + appName + "<br>";
28                     str += "Port: " + serverPort + "<br>";
29                     str += "Status: success</p>";
30                     return str;
31                 }
32 
33                 public String blockHandlerAnnoRes(BlockException exception) {
34                     String str = "<h2>Service Sentinel - Annotation Resource</h2>";
35                     str += "<p>Service Name: " + appName + "<br>";
36                     str += "Port: " + serverPort + "<br>";
37                     str += "Status: service restricted</p>";
38                     return str;
39                 }
40 
41             }


            通过 @SentinelResource 注解的 blockHandler 属性指定了一个 blockHandler 函数,进行限流之后的后续处理。

            使用 @SentinelResource 注解的 blockHandler 属性时,需要注意以下事项:

                (1) blockHandler 函数访问范围需要是 public;
                (2) 返回类型需要与原方法相匹配;
                (3) 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;
                (4) blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

        (2) 在 Sentinel 控制台配置流控规则

            启动 ServiceSentinel 模块,浏览器访问 “http://localhost:7001/service/anno”,显示结果如下。

                Service Sentinel - Annotation Resource
                Service Name: service-sentinel
                Port: 7001

                Status: success

            浏览器访问“http://localhost:8080”,登陆 Sentinel 控制台主页,点击 “service-sentinel” 下的 “簇点链路”,链路列表如下。

                > sentinel_spring_web_context
                    > /service/anno
                        annoRes

            点击 “/service/anno” 行的 “流控” 按钮,跳出 “新增流控规则” 对话框。新增流控规则,配置如下:

                资源名:/service/anno
                针对来源:default
                阈值类型:QPS
                单机阈值:1 (每秒 1 次)
                其它选项默认

            点击 “新增” 按钮,跳转到 “流控规则” 列表。快速连续(频率大于每秒 1 次)访问 “http://localhost:7001/service/anno”,结果如下。

                Blocked by Sentinel (flow limiting)

                注:以上提示是 Sentinel 系统自动生成的,显然,设置在 /service/anno 上的流控规则,无法调用到我们自定义的 blockHandlerAnnoRes,我们需要把流控规则设置到下一级的资源名 “annoRes” 上,在 “流控规则” 页面上删除这条规则。

            点击 “annoRes” 行的 “流控” 按钮,跳出 “新增流控规则” 对话框。新增流控规则,配置如下:          

                资源名:annoRes
                针对来源:default
                阈值类型:QPS
                单机阈值:1 (每秒 1 次)
                其它选项默认

            点击 “新增” 按钮,跳转到 “流控规则” 列表。快速连续(频率大于每秒 1 次)访问 “http://localhost:7001/service/anno”,结果如下。

                Service Sentinel - Annotation Resource
                Service Name: service-sentinel
                Port: 7001
                Status: service restricted


    2) 通过代码定义流控规则

        通过代码定义流控规则,就是不需要在 Sentinel 控制台定义流控规则,直接把流控规则写在代码里。

        这里继续在 ServiceSentinel 子模块上,修改代码演示流控规则。

        (1) 修改 src/main/java/com/example/controller/IndexController.java 文件

 1             package com.example.controller;
 2 
 3             import java.util.ArrayList;
 4             import java.util.List;
 5 
 6             import com.alibaba.csp.sentinel.annotation.SentinelResource;
 7             import org.springframework.beans.factory.annotation.Value;
 8             import org.springframework.web.bind.annotation.GetMapping;
 9             import org.springframework.web.bind.annotation.PathVariable;
10             import org.springframework.web.bind.annotation.RestController;
11 
12             import com.alibaba.csp.sentinel.slots.block.RuleConstant;
13             import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
14             import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
15 
16             import com.alibaba.csp.sentinel.Entry;
17             import com.alibaba.csp.sentinel.SphU;
18             import com.alibaba.csp.sentinel.SphO;
19             import com.alibaba.csp.sentinel.slots.block.BlockException;
20 
21             @RestController
22             public class IndexController {
23                 @Value("${server.port}")
24                 private String serverPort;
25                 @Value("${spring.application.name}")
26                 private String appName;
27 
28                 ...
29 
30                 public String blockHandlerAnnoRes(BlockException exception) {
31                     String str = "<h2>Service Sentinel - Annotation Resource 2</h2>";
32                     str += "<p>Service Name: " + appName + "<br>";
33                     str += "Port: " + serverPort + "<br>";
34                     str += "Status: service restricted</p>";
35                     return str;
36                 }
37 
38                 @GetMapping(value = "/service/anno2")
39                 @SentinelResource(value = "annoRes2", blockHandler = "blockHandlerAnnoRes")
40                 public String annoRes2() {
41                     initFlowRules();
42 
43                     String str = "<h2>Service Sentinel - Annotation Resource 2</h2>";
44                     str += "<p>Service Name: " + appName + "<br>";
45                     str += "Port: " + serverPort + "</p>";
46                     str += "Status: success</p>";
47                     return str;
48                 }
49 
50                 private static void initFlowRules() {
51                     List<FlowRule> rules = new ArrayList<>();
52                     FlowRule rule = new FlowRule();
53                     rule.setResource("annoRes2");
54                     rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
55                     rule.setCount(1);
56                     rules.add(rule);
57                     FlowRuleManager.loadRules(rules);
58                 }
59             }


            调用 FlowRuleManager 类的 loadRules() 方法来定义流控规则,该方法需要一个 FlowRule 类型的 List 集合作为其参数。

            FlowRule 可以通过以下属性定义流控规则,如下表。

属性 描述
resource 资源名,即流控规则的作用对象。
count 限流的阈值。
grade 流控阈值的类型,包括 QPS 或并发线程数。默认值:QPS
limitApp 流控针对的调用来源。默认值:default,表示不区分调用来源。
strategy 调用关系限流策略,包括直接、链路和关联。默认值:直接
controlBehavior 流控效果(直接拒绝、Warm Up、匀速排队),不支持按调用关系限流。默认值:直接拒绝


        (2) 运行

            启动 ServiceSentinel 模块,快速连续(频率大于每秒 1 次)访问 “http://localhost:7001/service/anno2”,结果如下。

                Service Sentinel - Annotation Resource 2
                Service Name: service-sentinel
                Port: 7001
                Status: service restricted


2. 熔断降级

    除了流量控制以外,对调用链路中不稳定资源的熔断降级,也是保障服务高可用的重要措施之一。

    在分布式微服务架构中,一个系统往往由多个服务组成,不同服务之间相互调用,组成复杂的调用链路。如果链路上的某一个服务出现故障,那么故障就会沿着调用链路在系统中蔓延,最终导致整个系统瘫痪。Sentinel 提供了熔断降级机制就可以解决这个问题。

    Sentinel 的熔断将机制会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),暂时切断对这个资源的调用,以避免局部不稳定因素导致整个系统的雪崩。

    熔断降级作为服务保护自身的手段,通常在客户端(调用端)进行配置,资源被熔断降级最直接的表现就是抛出 DegradeException 异常。

    Sentinel 提供了 3 种熔断策略,如下表所示。

        (1) 慢调用比例 (SLOW_REQUEST_RATIO): 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大响应时间),若请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则再次被熔断。

        (2) 异常比例 (ERROR_RATIO): 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目且异常的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

        (3) 异常数 (ERROR_COUNT): 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

        注意:Sentinel 1.8.0 版本对熔断降级特性进行了全新的改进升级,以上熔断策略针对的是 Sentinel 1.8.0 及以上版本。

    Sentinel 熔断降级中共涉及 3 种状态,熔断状态的之间的转换过程如下。

        (1) 熔断关闭状态 (CLOSED): 处于关闭状态时,请求可以正常调用资源。

            满足以下任意条件,Sentinel 熔断器进入熔断关闭状态:

                a) 全部请求访问成功。
                b) 单位统计时长(statIntervalMs)内请求数目小于设置的最小请求数目。
                c) 未达到熔断标准,例如服务超时比例、异常数、异常比例未达到阈值。
                d) 处于探测恢复状态时,下一个请求访问成功。

        (2) 熔断开启状态 (OPEN): 处于熔断开启状态时,熔断器会一定的时间(规定的熔断时长)内,暂时切断所有请求对该资源的调用,并调用相应的降级逻辑使请求快速失败避免系统崩溃。

            满足以下任意条件,Sentinel 熔断器进入熔断开启状态:

                a) 单位统计时长内请求数目大于设置的最小请求数目,且已达到熔断标准,例如请求超时比例、异常数、异常比例达到阈值。
                b) 处于探测恢复状态时,下一个请求访问失败。

        (3) 探测恢复状态 (HALF-OPEN): 处于探测恢复状态时,Sentinel 熔断器会允许一个请求调用资源。则若接下来的一个请求成功完成(没有错误)则结束熔断,熔断器进入熔断关闭(CLOSED)状态;否则会再次被熔断,熔断器进入熔断开启(OPEN)状态。

            在熔断开启一段时间(降级窗口时间或熔断时长,单位为 s)后,Sentinel 熔断器自动会进入探测恢复状态。

    Sentinel 熔断降级规则包含多个重要属性,如下表所示。

属性 描述
资源名 规则的作用对象。使用范围:所有熔断策略
熔断策略 Sentinel 支持3 中熔断策略:慢调用比例、异常比例、异常数策略。默认值:慢调用比例,使用范围:所有熔断策略
最大 RT 请求的最大相应时间,请求的响应时间大于该值则统计为慢调用。使用范围:慢调用比例
熔断时长 熔断开启状态持续的时间,超过该时间熔断器会切换为探测恢复状态(HALF-OPEN),单位为 s。使用范围:所有熔断策略。
最小请求数 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)。默认值:5,使用范围:所有熔断策略。
统计时长 熔断触发需要统计的时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)。默认值:1000 ms,使用范围:所有熔断策略。
比例阈值 分为慢调用比例阈值和异常比例阈值,即慢调用或异常调用占所有请求的百分比,取值范围 [0.0,1.0]。使用范围:慢调用比例 、异常比例。
异常数 请求或调用发生的异常的数量。使用范围:异常数。


    Sentinel 实现熔断降级过程,步骤如下。

        (1) 在项目中,使用 @SentinelResource 注解的 fallback 属性可以为资源指定熔断降级逻辑(方法)。
        (2) 通过 Sentinel 控制台或代码定义熔断规则,包括熔断策略、最小请求数、阈值、熔断时长以及统计时长等。
        (3) 若单位统计时长(statIntervalMs)内,请求数目大于设置的最小请求数目且达到熔断标准(例如请求超时比例、异常数、异常比例达到阈值),Sentinel 熔断器进入熔断开启状态(OPEN)。
        (4) 处于熔断开启状态时, @SentinelResource 注解的 fallback 属性指定的降级逻辑会临时充当主业务逻辑,而原来的主逻辑则暂时不可用。当有请求访问该资源时,会直接调用降级逻辑使请求快速失败,而不会调用原来的主业务逻辑。
        (5) 在经过一段时间(在熔断规则中设置的熔断时长)后,熔断器会进入探测恢复状态(HALF-OPEN),此时 Sentinel 会允许一个请求对原来的主业务逻辑进行调用,并监控其调用结果。
        (6) 若请求调用成功,则熔断器进入熔断关闭状态(CLOSED ),服务原来的主业务逻辑恢复,否则重新进入熔断开启状态(OPEN)。


    1) 通过 Sentinel 控制台定义熔断降级规则

        通过 Sentinel 控制台直接对资源定义熔断降级规则,我们在 ServiceSentinel 子模块上,修改代码演示熔断降级规则。

        (1) 创建 src/main/java/com/example/controller/FusingController.java 文件

 1             package com.example.controller;
 2 
 3             import java.util.Date;
 4             import java.text.SimpleDateFormat;
 5 
 6             import com.alibaba.csp.sentinel.annotation.SentinelResource;
 7             import org.springframework.beans.factory.annotation.Value;
 8             import org.springframework.web.bind.annotation.GetMapping;
 9             import org.springframework.web.bind.annotation.PathVariable;
10             import org.springframework.web.bind.annotation.RestController;
11 
12             import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
13             import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
14             import com.alibaba.csp.sentinel.util.TimeUtil;
15 
16             @RestController
17             public class FusingController {
18                 @Value("${server.port}")
19                 private String serverPort;
20                 @Value("${spring.application.name}")
21                 private String appName;
22 
23                 @GetMapping(value = "/fusing/res/{id}")
24                 @SentinelResource(value = "fusingRes", fallback = "handlerFallback")
25                 public String fusingRes(@PathVariable("id") int id) {
26                     monitor();
27                     System.out.println("FusingController -> fusingRes(): monitoring ...");
28 
29                     if (id == -1) {
30                         System.err.println("FusingController -> fusingRes(): illegal argument exception");
31                         throw new IllegalArgumentException("Illegal argument exception");
32                     } else if (id == -2) {
33                         System.err.println("FusingController -> fusingRes(): null pointer exception");
34                         throw new NullPointerException("Null pointer exception");
35                     }
36 
37                     String str = "<h2>Service Sentinel - Fusing Resource</h2>";
38                     str += "<p>Service Name: " + appName + "<br>";
39                     str += "Port: " + serverPort + "<br>";
40                     str += "Id: " + id + "<br>";
41                     str += "Status: success</p>";
42                     return str;
43                 }
44 
45                 // 处理异常的回退方法(服务降级)
46                 public String handlerFallback(@PathVariable("id") int id, Throwable e) {
47                     String str = "<h2>Service Sentinel - Fusing Resource</h2>";
48                     str += "<p>Service Name: " + appName + "<br>";
49                     str += "Port: " + serverPort + "<br>";
50                     str += "Id: " + id + "<br>";
51                     str += "Status: fallback</p>";
52                     return str;
53                 }
54 
55                 public void monitor() {
56                     EventObserverRegistry.getInstance().addStateChangeObserver("logging",
57                         (prevState, newState, rule, snapshotValue) -> {
58                             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
59                             if (newState == CircuitBreaker.State.OPEN) {
60                                 // 变换至 OPEN state 时会携带触发时的值
61                                 System.err.println(String.format("%s -> OPEN at %s, request times = %.2f", prevState.name(),
62                                         format.format(new Date(TimeUtil.currentTimeMillis())), snapshotValue));
63                             } else {
64                                 System.err.println(String.format("%s -> %s at %s", prevState.name(), newState.name(),
65                                         format.format(new Date(TimeUtil.currentTimeMillis()))));
66                             }
67                         });
68                 }
69             }


            使用 @SentinelResource 注解的 fallback 属性时,需要注意以下事项:

                a) 返回值类型必须与原函数返回值类型一致;
                b) 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
                c) fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

        (2) 在 Sentinel 控制台配置熔断降级规则

            启动 ServiceSentinel 模块,浏览器访问 http://localhost:7001/fusing/res/2,显示结果如下:

                Service Sentinel - Fusing Resource
                Service Name: service-sentinel
                Port: 7001
                Id: 2
                Status: success               

            浏览器访问 “http://localhost:8080”,登陆 Sentinel 控制台主页,点击 “service-sentinel” 下的 “簇点链路”,链路列表如下。

                > sentinel_spring_web_context
                    > /fusing/res/{id}
                        fusingRes

            点击 “fallback” 行的 “熔断” 按钮,跳出 “新增熔断规则” 对话框。新增熔断规则,配置如下:          

                资源名:fallback
                熔断策略:异常数
                异常数:1
                熔断时长:5 秒
                最小请求数: 2
                统计时长: 1000 毫秒

            点击 “新增” 按钮,跳转到 “熔断规则” 列表。快速连续(频率大于每秒 2 次)访问 “http://localhost:7001/fusing/res/-1” (或 http://localhost:7001/fusing/res/-2),结果如下。

                Service Sentinel - Fusing Resource
                Service Name: service-sentinel
                Port: 7001
                Id: -1
                Status: fallback

            立即访问 http://localhost:7001/fusing/res/2,Status 也显示 fallback (处于熔断状态),等待几秒,再次访问 http://localhost:7001/fusing/res/2,Status 显示 success。


    2) 通过代码定义熔断规则

        通过代码定义熔断规则,就是不需要在 Sentinel 控制台定义熔断规则,直接把熔断规则写在代码里。

        这里继续在 ServiceSentinel 子模块上,修改代码演示流控规则。

        (1) 修改 src/main/java/com/example/controller/FusingController.java 文件

 1             package com.example.controller;
 2 
 3             import java.util.Date;
 4             import java.text.SimpleDateFormat;
 5 
 6             import com.alibaba.csp.sentinel.annotation.SentinelResource;
 7             import org.springframework.beans.factory.annotation.Value;
 8             import org.springframework.web.bind.annotation.GetMapping;
 9             import org.springframework.web.bind.annotation.PathVariable;
10             import org.springframework.web.bind.annotation.RestController;
11 
12             import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
13             import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
14             import com.alibaba.csp.sentinel.util.TimeUtil;
15 
16             @RestController
17             public class FusingController {
18                 @Value("${server.port}")
19                 private String serverPort;
20                 @Value("${spring.application.name}")
21                 private String appName;
22 
23                 @GetMapping(value = "/fusing/res/{id}")
24                 @SentinelResource(value = "fusingRes", fallback = "handlerFallback")
25                 public String fusingRes(@PathVariable("id") int id) {
26                     initDegradeRule();
27                     monitor();
28                     System.out.println("FusingController -> fusingRes(): monitoring ...");
29 
30                     if (id == -1) {
31                         System.err.println("FusingController -> fusingRes(): illegal argument exception");
32                         throw new IllegalArgumentException("Illegal argument exception");
33                     } else if (id == -2) {
34                         System.err.println("FusingController -> fusingRes(): null pointer exception");
35                         throw new NullPointerException("Null pointer exception");
36                     }
37 
38                     String str = "<h2>Service Sentinel - Fusing Resource</h2>";
39                     str += "<p>Service Name: " + appName + "<br>";
40                     str += "Port: " + serverPort + "<br>";
41                     str += "Id: " + id + "<br>";
42                     str += "Status: success</p>";
43                     return str;
44                 }
45 
46                 // 处理异常的回退方法(服务降级)
47                 public String handlerFallback(@PathVariable("id") int id, Throwable e) {
48                     String str = "<h2>Service Sentinel - Fusing Resource</h2>";
49                     str += "<p>Service Name: " + appName + "<br>";
50                     str += "Port: " + serverPort + "<br>";
51                     str += "Id: " + id + "<br>";
52                     str += "Status: fallback</p>";
53                     return str;
54                 }
55 
56                 public void monitor() {
57                     EventObserverRegistry.getInstance().addStateChangeObserver("logging",
58                         (prevState, newState, rule, snapshotValue) -> {
59                             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
60                             if (newState == CircuitBreaker.State.OPEN) {
61                                 // 变换至 OPEN state 时会携带触发时的值
62                                 System.err.println(String.format("%s -> OPEN at %s, request times = %.2f", prevState.name(),
63                                         format.format(new Date(TimeUtil.currentTimeMillis())), snapshotValue));
64                             } else {
65                                 System.err.println(String.format("%s -> %s at %s", prevState.name(), newState.name(),
66                                         format.format(new Date(TimeUtil.currentTimeMillis()))));
67                             }
68                         });
69                 }
70 
71                 private static void initDegradeRule() {
72                     List<DegradeRule> rules = new ArrayList<>();
73                     DegradeRule rule = new DegradeRule("fusingRes");
74                     rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType()); // 熔断策略为异常比例                  
75                     rule.setCount(0.6);         // 异常比例阈值
76                     rule.setMinRequestAmount(5);  // 最小请求数
77                     rule.setStatIntervalMs(10000);  // 统计时长,单位毫秒
78                     rule.setTimeWindow(10); // 熔断时长,单位秒
79                     rules.add(rule);
80                     DegradeRuleManager.loadRules(rules);
81                 }
82 
83             }


         (2) 启动 ServiceSentinel 模块

            a) 测试1

                浏览器访问 “http://localhost:7001/fusing/res/-1” 一次,结果显示 Status:fallback。

                立即访问 “http://localhost:7001/fusing/res/2” 一次,结果显示 Status: success。

                此时异常比率 0.5 < 0.6,请求次数 2 < 5,异常比率和请求次数都不满足熔断条件。

                等待 10 秒后,进入下一轮测试,下同。

            b) 测试2

                连续快速访问 “http://localhost:7001/fusing/res/-1”  四次,结果显示 Status:fallback。

                立即访问 “http://localhost:7001/fusing/res/2” 一次,结果显示 Status: success。

                此时异常比率 0.8 > 0.6,请求次数 5 == 5,请求次数不满足熔断条件。

            c) 测试3

                连续快速访问 “http://localhost:7001/fusing/res/-1”  五次,结果显示 Status:fallback。

                立即访问 “http://localhost:7001/fusing/res/2” 一次,结果显示 Status: fallback。

                此时异常比率 0.83 > 0.6,请求次数 6 > 5,满足熔断条件。


posted @ 2022-07-22 08:25  垄山小站  阅读(289)  评论(0)    收藏  举报