上篇文章我们讲解了Sentinel 流控规则的配制,本篇文章我们继续讲下Sentinel 的熔断降级,依然采用程序配制方式实现。
上篇文章地址:https://blog.csdn.net/qq_43692950/article/details/122159806
由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。这个问题和 Hystrix 里面描述的问题是一样的。
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
Sentinel 提供了基于异常比例、异常数、慢调用比例的策略,下面来看下实现:
新建熔断规则了:
@PostConstruct
public void initDegrade() {
DegradeRule rule = new DegradeRule();
rule.setResource("errdegrade");
rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType()); //熔断策略,异常比例
rule.setCount(0.5d); //异常比例的阈值
rule.setStatIntervalMs(10000); //统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)
rule.setMinRequestAmount(4); //熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)
rule.setTimeWindow(5); //熔断时长,单位为 s
List rules = new ArrayList<>();
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
上面实现了基于异常比例的熔断规则,在统计时长10秒的时间,请求数达到4次,如果有50%的请求异常,则进入熔断状态,下面的请求全都进入降级方法,熔断时间为5秒,每5秒则释放一个请求测试接口是否恢复正常,如果接口恢复正常则停止熔断。
下面在编写测试接口前,先创建个全局的降级回调方法。
public class CustomerBlockHandler {
public static ResponseTemplate blockHandler(BlockException exception) {
return ResFailTemplate.builder().code(406).message("接口限流!").build();
}
public static ResponseTemplate fallback() {
return ResFailTemplate.builder().code(406).message("服务降级!").build();
}
}
下面编写测试接口:
@RestController
@RequestMapping("/circuitBreaker")
public class CircuitBreakerController {
@SentinelResource(value = "errdegrade",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "blockHandler",
fallbackClass = CustomerBlockHandler.class,
fallback = "fallback")
@GetMapping("/errdegrade")
public ResponseTemplate errdegrade() throws InterruptedException {
System.out.println("请求了!");
int a = 1 / 0;
return ResSuccessTemplate.builder().build();
}
}
下面编写测试请求程序:
@Slf4j
public class TestDegrade {
public static void main(String[] args) throws InterruptedException {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/circuitBreaker/errdegrade";
for (int i = 1; i <= 20; i++) {
long t = System.currentTimeMillis();
String forObject = restTemplate.getForObject(url, String.class);
log.info("请求次数:{} , 返回结果:{} , 耗时:{}", i, forObject, (System.currentTimeMillis() - t));
Thread.sleep(500);
}
}
}
调用测试程序,查看打印日志:
可以看出大部分请求都已经被熔断直接进入了降级方法。
修改下上面的规则:
@PostConstruct
public void initDegrade() {
DegradeRule rule = new DegradeRule();
rule.setResource("errdegrade");
rule.setGrade(CircuitBreakerStrategy.ERROR_COUNT.getType()); //熔断策略,异常数
rule.setCount(2d); //异常数阈值
rule.setStatIntervalMs(10000); //统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)
rule.setMinRequestAmount(4); //熔断触发的最小请求数,请求数小于该值时即使异常数超出阈值也不会熔断(1.7.0 引入)
rule.setTimeWindow(5); //熔断时长,单位为 s
List rules = new ArrayList<>();
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
上面的规则表示,在统计时长10秒的时间内,请求数达到4次,并且有2次错误请求,即进入熔断状态,5秒后尝试释放一个请求探测接口是否正常,如果正常则结束熔断。
还是使用上面的测试程序进行测试:
修改熔断规则:
@PostConstruct
public void initDegrade() {
DegradeRule rule = new DegradeRule();
rule.setResource("errdegrade");
rule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()); //熔断策略,慢调用比例
rule.setCount(1500d); //慢调用临界 RT(超出该值计为慢调用)
rule.setStatIntervalMs(10000); //统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)
rule.setMinRequestAmount(4); //熔断触发的最小请求数,请求数小于该值时即使异常数超出阈值也不会熔断(1.7.0 引入)
rule.setTimeWindow(5); //熔断时长,单位为 s
rule.setSlowRatioThreshold(0.5d); //慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
List rules = new ArrayList<>();
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
上面规则表示的意思,在10秒的时间,请求数达到了4次,并且有50%的接口处理时间超过了1.5秒的时间,则进入熔断状态,每隔5面释放一个请求用来探测接口是否已正常。
下面修改测试接口:
@RestController
@RequestMapping("/circuitBreaker")
public class CircuitBreakerController {
@SentinelResource(value = "errdegrade",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "blockHandler",
fallbackClass = CustomerBlockHandler.class,
fallback = "fallback")
@GetMapping("/errdegrade")
public ResponseTemplate errdegrade() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
System.out.println("请求了!");
return ResSuccessTemplate.builder().build();
}
}
Sentinel他还提供了熔断状态切换的监听,可以做一些自定义的逻辑:
@PostConstruct
public void ruleObserver() {
EventObserverRegistry.getInstance().addStateChangeObserver("logging",
(prevState, newState, thatRule, snapshotValue) -> {
log.info("{} 熔断状态改变,当前状态:{} ", thatRule.getResource(), newState);
});
}
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!