Soul网关源码探秘《十八》 - Hystrix插件源码分析

前文探讨了如何开启 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 的官方网站,学习了一下它的执行流程。
Hystrix执行流程

  1. 构建一个 HystrixCommand 或者 HystrixObservableCommand 对象
  2. 执行命令
  3. 判断结果是否已缓存,如果已缓存,直接把结果返回
  4. 判断短路器是否已打开
  5. 判断线程池/队列/信号量是否已达到阈值
  6. 如果4和5的答案都为否,则执行HystrixObservableCommand.construct() 或者 HystrixCommand.run()方法
  7. 如果执行失败或者超时,则计算是否该打开短路器
  8. 出现以下三种情况则调用回调地址:短路器已打开、线程池/队列/信号量已达到阈值、construct()/run()方法执行失败或者超时
  9. 返回成功的响应

再次回到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 中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rughru

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值