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,满足熔断条件。