一、目标
1、解读熔断插件关键代码,对比各熔断插件;
2、排查上一节使用过程中遇到的问题;
二、内容
2.1背景
上一节我们一起学习了Soul网关中hystrix插件的使用,并且在使用的过程中发现了一些问题,这一节我们就一起来学习一下hystrix熔断插件的关键代码实现。对比hystrix插件与其他几个熔断插件。排查使用过程中遇到的问题。
有关hystrix插件的使用,可以参考上一节的内容:
Soul网关现在支持熔断的插件有:
- hystrix
- resilienc4j
- sentinel
2.2 HystrixPlugin插件分析
- HystrixPlugin继承了AbstractSoulPlugin类,重写了doExecute()方法。插件前面的调用链已经分析过了,可以参考之前的分析:
divide插件分析:https://blog.csdn.net/qq_38314459/article/details/112760726
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
//获取rule规则配置数据
final HystrixHandle hystrixHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), HystrixHandle.class);
//处理groupKey和commandKey为空的方法,如果groupKey为空,则默认设置为contextPath
if (StringUtils.isBlank(hystrixHandle.getGroupKey())) {
hystrixHandle.setGroupKey(Objects.requireNonNull(soulContext).getModule());
}
//如果commandKey为空,默认设置为具体的请求路径
if (StringUtils.isBlank(hystrixHandle.getCommandKey())) {
hystrixHandle.setCommandKey(Objects.requireNonNull(soulContext).getMethod());
}
//创建hystrixCommand
Command command = fetchCommand(hystrixHandle, exchange, chain);
return Mono.create(s -> {
Subscription sub = command.fetchObservable().subscribe(s::success,
s::error, s::success);
s.onCancel(sub::unsubscribe);
//判断是否当前已经进入熔断状态
if (command.isCircuitBreakerOpen()) {
log.error("hystrix execute have circuitBreaker is Open! groupKey:{},commandKey:{}", hystrixHandle.getGroupKey(), hystrixHandle.getCommandKey());
}
}).doOnError(throwable -> {
log.error("hystrix execute exception:", throwable);
exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.ERROR.getName());
chain.execute(exchange);
}).then();
}
- fetchCommand()方法分析,在创建command的过程中,会去判断是信号量还是线程,根据不同的类型创建
Command
:
private Command fetchCommand(final HystrixHandle hystrixHandle, final ServerWebExchange exchange, final SoulPluginChain chain) {
if (hystrixHandle.getExecutionIsolationStrategy() == HystrixIsolationModeEnum.SEMAPHORE.getCode()) {
return new HystrixCommand(HystrixBuilder.build(hystrixHandle),
exchange, chain, hystrixHandle.getCallBackUri());
}
return new HystrixCommandOnThread(HystrixBuilder.buildForHystrixCommand(hystrixHandle),
exchange, chain, hystrixHandle.getCallBackUri());
}
HystrixCommand类分析,HystrixCommand继承了HystrixObservableCommand,实现了Command接口。这里面主要的方法是fetchObservable()方法:
@Override
public Observable<Void> fetchObservable() {
return this.toObservable();
}
每次执行的时候加上监听,其中this.execute()方法调用的hystrix自身提供的方法;
如果达到熔断的条件,会调用Command里面的doFallBack方法:
default Mono<Void> doFallback(ServerWebExchange exchange, Throwable exception) {
if (Objects.isNull(getCallBackUri())) {
Object error;
error = generateError(exchange, exception);
return WebFluxResultUtils.result(exchange, error);
}
DispatcherHandler dispatcherHandler =
SpringBeanUtils.getInstance().getBean(DispatcherHandler.class);
ServerHttpRequest request = exchange.getRequest().mutate().uri(getCallBackUri()).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
return dispatcherHandler.handle(mutated);
}
2.3 熔断插件对比
Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性;可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况;Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合;Sentinel 提供简单易用、完善的 SPI 扩展接口,可以通过扩展接口来定制业务逻辑。
Hystrix是一个延迟和容错库,旨在隔离远程系统、服务和第三方库的访问点,停止级联故障,并在故障不可避免的复杂分布式系统中实现恢复能力;
Resilience4j是一个为Java8和函数式编程设计的容错库。提供高阶函数(decorators)来增强任何功能接口、lambda表达式或方法引用,包括断路器、速率限制器、重试或隔板。可以在任何函数接口、lambda表达式或方法引用上堆叠多个装饰器。优点是您可以选择所需的装饰器,而无需其他任何东西。
2.4 上一节报错原因分析
报错是由于没有找到divide插件的upstream造成的
打开soul-admin控制台,发现divide插件选择器的配置的确为空了
三、总结
Soul网关支持hystrix、sentinel、resilienc4j三个熔断插件,着重分析了hystrix插件的源码。简单对比了三个插件的区别和功能差别。三个插件的使用,有共同的地方,也有差异的部分,一起对比来看会更有收获。