前文探讨了如何开启 Hystrix 插件,以及如何配置及使用该插件。今天来研究一下 Hystrix 插件在插件链中的执行逻辑,以及看它是如何实现熔断降级等功能的。
准备工作
启动soul-admin项目,并在后台页面开启divide和hystrix插件。
启动soul-bootstrap项目以及soul-examples-http项目。
源码分析
在前文分析插件链时知道了,每一个插件都各自实现自己的 doExecute 方法。我们直接找到 Hystrix 插件中的该方法。并在该方法上打上断点。然后在终端执行压测指令wrk -t 8 -c 32 -d 60s http://192.168.31.34:8192/http/order/findById\?id\=5
。确保程序会进入hystrix插件的熔断逻辑。
public class HystrixPlugin extends AbstractSoulPlugin {
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
// ...
// 找到配置规则中 hystrix 的相关配置
final HystrixHandle hystrixHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), HystrixHandle.class);
// ...
}
// 根据设置的规则生成 command 对象
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 -> {
// 执行下一个插件逻辑
chain.execute(exchange);
}).then();
}
}
fetchCommand方法根据设置的规则生成了 command 对象。中间那段看不懂的代码command.fetchObservable().subscribe(s::success,s::error, s::success);
应该是订阅了一个事件,做了一系列判断。先不管它,先看看fetchCommand方法做了什么。
private Command fetchCommand(final HystrixHandle hystrixHandle, final ServerWebExchange exchange, final SoulPluginChain chain) {
if (hystrixHandle.getExecutionIsolationStrategy() == HystrixIsolationModeEnum.SEMAPHORE.getCode()) {
// 如果在规则中设置为信号量 SEMAPHORE 的方式,则生成一个 HystrixCommand 对象
return new HystrixCommand(HystrixBuilder.build(hystrixHandle),
exchange, chain, hystrixHandle.getCallBackUri());
}
// 其他的生成一个 HystrixCommandOnThread 对象
return new HystrixCommandOnThread(HystrixBuilder.buildForHystrixCommand(hystrixHandle),
exchange, chain, hystrixHandle.getCallBackUri());
}
fetchCommand方法中根据规则中是否设置为信号量SEMAPHORE 的方式,生成不同的 command 对象。
Hystrix有两种隔离策略,线程隔离和信号量隔离,在soul中,也是在fetchCommand方法中根据规则使用不同的隔离策略。
Hystrix 执行流程
这里就不是很清楚接下来的流程逻辑了,到 Hystrix 的官方网站,学习了一下它的执行流程。
- 构建一个 HystrixCommand 或者 HystrixObservableCommand 对象
- 执行命令
- 判断结果是否已缓存,如果已缓存,直接把结果返回
- 判断短路器是否已打开
- 判断线程池/队列/信号量是否已达到阈值
- 如果4和5的答案都为否,则执行HystrixObservableCommand.construct() 或者 HystrixCommand.run()方法
- 如果执行失败或者超时,则计算是否该打开短路器
- 出现以下三种情况则调用回调地址:短路器已打开、线程池/队列/信号量已达到阈值、construct()/run()方法执行失败或者超时
- 返回成功的响应
再次回到soul的HystrixCommand类中,fetchObservable()方法等同于上面的第二步。
public Observable<Void> fetchObservable() {
return this.toObservable();
}
@Override
protected Observable<Void> construct() {
return RxReactiveStreams.toObservable(chain.execute(exchange));
}
同时看到,construct()方法如果执行返回成功的响应,则会直接执行下一个插件的逻辑。
而执行回调的逻辑在以下代码中。
protected Observable<Void> resumeWithFallback() {
return RxReactiveStreams.toObservable(doFallback());
}
private Mono<Void> doFallback() {
if (isFailedExecution()) {
log.error("hystrix execute have error: ", getExecutionException());
}
final Throwable exception = getExecutionException();
return doFallback(exchange, exception);
}
最终执行 doFallback 的逻辑在 command 接口中。
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);
}
线程隔离的HystrixCommandOnThread逻辑是类似的,就不再赘述了。
总结
今天分析了 Hystrix 的执行流程,以及在 soul 网关中 Hystrix 插件的主要作用和执行逻辑。可以看出,soul网关中的 Hystrix 插件并没有提供额外的功能,后续执行逻辑还是在 Hystrix 中。