在上一篇文章《Soul网关源码学习(15)- HystrixPlugin 分析》中,我们分析了 soul 集成 hystrix的原理,这一章我们再来分析一下 Resilience4J 到底是如果通过 Resilience4JPlugin 集成到 soul 里面的。
Resilience4j是受到Netflix Hystrix的启发,为Java8和函数式编程所设计的轻量级容错框架。整个框架只是使用了Varr的库,不需要引入其他的外部依赖。与此相比,Netflix Hystrix对Archaius具有编译依赖,而Archaius需要更多的外部依赖,例如Guava和Apache Commons Configuration。
Resilience4j提供了提供了一组高阶函数(装饰器),包括断路器,限流器,重试机制,隔离机制。你可以使用其中的一个或多个装饰器对函数式接口,lambda表达式或方法引用进行装饰。这么做的优点是你可以选择所需要的装饰器进行装饰。
在使用Resilience4j的过程中,不需要引入所有的依赖,只引入需要的依赖即可。
Resilience4JPlugin 是继承于模板类 AbstractSoulPlugin,所以 doExecutor 是其执行真正功能逻辑的代码:
@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;
Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class);
//如果开启熔断配置
if (resilience4JHandle.getCircuitEnable() == 1) {
return combined(exchange, chain, rule);
}
//如果不开启熔断就只使用 Resilience4J 的限流组件
return rateLimiter(exchange, chain, rule);
}
上面代码主要有两点:
combined 方法:
private Mono<Void> combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) {
//创建Resilience4J相关配置
Resilience4JConf conf = Resilience4JBuilder.build(rule);
return combinedExecutor.run(
//插件链后续执行
chain.execute(exchange)
.doOnSuccess(v -> {
...
}),
//降级处理
fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf);
}
combinedExecutor 是 soul 自己封装的执行器,下面是器 run 方法:
@Override
public <T> Mono<T> run(final Mono<T> run, final Function<Throwable, Mono<T>> fallback, final Resilience4JConf resilience4JConf) {
//创建限流组件
RateLimiter rateLimiter = Resilience4JRegistryFactory.rateLimiter(resilience4JConf.getId(), resilience4JConf.getRateLimiterConfig());
//创建熔断组件
CircuitBreaker circuitBreaker = Resilience4JRegistryFactory.circuitBreaker(resilience4JConf.getId(), resilience4JConf.getCircuitBreakerConfig());
//Resilience4J 函数是支持多组件链式调用的
//首先执行熔断器
Mono<T> to = run.transformDeferred(CircuitBreakerOperator.of(circuitBreaker))
//再执行限流
.transformDeferred(RateLimiterOperator.of(rateLimiter))
...
if (fallback != null) {
//降级处理
to = to.onErrorResume(fallback);
}
return to;
}
这是真正使用 Resilience4J 功能的核心代码:创建 Resilience4J 熔断器和限流器,并且通过函数式链式调用把 Soul 后续的执行任务包装进去。
接下来我们在看一下 Resilience4J 不同修饰器配置创建的地方,回到上面 combined 方法,我们很容易就知道了 Resilience4JBuilder 就是我们要找的类:
// Resilience4JBuilder build 方法,省略了部分设置代码
public static Resilience4JConf build(final RuleData ruleData) {
Resilience4JHandle handle = GsonUtils.getGson().fromJson(ruleData.getHandle(), Resilience4JHandle.class);
CircuitBreakerConfig circuitBreakerConfig = null;
if (handle.getCircuitEnable() == 1) {
//熔断组件配置
circuitBreakerConfig = CircuitBreakerConfig.custom()
//熔断失败判定:所有 Throwable 和 Throwable 都会被判定失败
.recordExceptions(Throwable.class, Throwable.class)
...
//断路器在半开状态下允许通过的调用次数。
.permittedNumberOfCallsInHalfOpenState(handle.getPermittedNumberOfCallsInHalfOpenState()).build();
}
//限时组件配置
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
//配置任务执行超时限制
.timeoutDuration(Duration.ofSeconds(handle.getTimeoutDuration() / 1000)).build();
//限流组件配置
RateLimiterConfig rateLimiterConfig = RateLimiterConfig.custom()
...
//限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod
.limitRefreshPeriod(Duration.ofNanos(handle.getLimitRefreshPeriod() * 1000000)).build();
return new Resilience4JConf(Resilience4JHandler.getResourceName(ruleData), handle.getFallbackUri(), rateLimiterConfig, timeLimiterConfig, circuitBreakerConfig);
}
到这里我们小结一下 Resilience4JPlugin 组合熔断和限流的流程:
分析完熔断和限流的组合使用后,那后面的单一限流的实现应该就更加简单了,过程大同小异只是少了熔断器而已,其方法入口是 rateLimiter,有兴趣的小伙伴可以自己去跟踪一下,这里就不重复分析了。
到这里 Resilience4J 的集成原理也分析完了,相对于上一篇 Hystrix 的集成分析,这篇明显简单了许多。这可能是得益于 Resilience4J 简单易用的 API 设计(组合功能 + 链式调用)。后面熔断框架集成的源码分析就只剩下最后关于 sentinel 的一篇了,这个后续会继续带来的。