【高性能网关soul学习】17. 熔断限流插件之Resilienc4j
本文目标:
- 跑通熔断限流插件 Resilienc4j
- 分析 Soul 中 Resilienc4j 是如何集成的
Resilienc4j
Resilience4j是一个轻量级容错框架,设计灵感来源于Netflix 的Hystrix框架,为函数式编程所设计。
Resilience4j 提供了一组高阶函数(装饰器),包括断路器,限流器,重试,隔离,可以对任何的函数式接口,lambda表达式,或方法的引用进行增强,并且这些装饰器可以进行叠加。这样做的好处是,你可以根据需要选择特定的装饰器进行组合。
在使用时,你不需要引入所有和Resilience4j相关的包,只需要引入所需要的即可。
Soul 中 Resilienc4j 插件启动流程
- soul-bootstrap 增加 soul-spring-boot-starter-plugin-resilience4j 依赖
- 依次启动 soul-admin、http测试服务、soul-bootstrap 网关服务
- 选择器和规则配置
- 参考 选择器配置规则
- 大致的步骤与 hystirx 插件的配置方式类似,这里不再重复
Resilience4JPlugin
Resilience4JPlugin 和其他 plugin 一样,都属于流量插件,因此继承于 AbstractSoulPlugin,因此核心方法为 doExecute
- admin控制台上的
circuit enable
为 1时,启动熔断功能
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);
}
// 只使用限流的组件
return rateLimiter(exchange, chain, rule);
}
因为 combined 方法里面包含了rateLimiter的功能,因此我们下面重点分析 combined 方法
- 首先使用 rule 构建了一个配置信息
- 创建了熔断器和限流器
- 然后执行 combinedExecutor 的run方法
- 里面封装了熔断器、限流器的执行操作,以及对错误的捕获和降级逻辑等
private Mono<Void> combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) {
// 根据rule配置信息,创建 Resilience4J 相关配置
Resilience4JConf conf = Resilience4JBuilder.build(rule);
return combinedExecutor.run(
// 插件链执行完毕之后执行结果判断逻辑
chain.execute(exchange).doOnSuccess(v -> {
... check 抛出一个异常,后续捕获
// 降级处理
}), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf);
}
// Resilience4JBuilder.java 大致流程
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 和 Exception 都会被判定为失败
.recordExceptions(Throwable.class, Exception.class)
// 其他的一些参数配置设置
... ;
}
// 时间限制配置
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(handle.getTimeoutDuration() / 1000)).build();
// 限流组件配置
RateLimiterConfig rateLimiterConfig = RateLimiterConfig.custom()
.limitForPeriod(handle.getLimitForPeriod())
.timeoutDuration(Duration.ofSeconds(handle.getTimeoutDurationRate() / 1000))
.limitRefreshPeriod(Duration.ofNanos(handle.getLimitRefreshPeriod() * 1000000)).build();
return new Resilience4JConf(Resilience4JHandler.getResourceName(ruleData), handle.getFallbackUri(), rateLimiterConfig, timeLimiterConfig, circuitBreakerConfig);
}
// CombinedExecutor.java
@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());
Mono<T> to = run
// 执行熔断器操作
.transformDeferred(CircuitBreakerOperator.of(circuitBreaker))
// 执行限流操作操作
.transformDeferred(RateLimiterOperator.of(rateLimiter))
.timeout(resilience4JConf.getTimeLimiterConfig().getTimeoutDuration())
// 出现错误时,熔断器捕获异常,后续转为各种event进行处理
.doOnError(...);
// 降级操作
if (fallback != null) {
to = to.onErrorResume(fallback);
}
return to;
}
总结
- Resilienc4j 插件根据开关的打开情况,支持只使用限流配置或者 限流+熔断 两种配置方式
- Resilienc4j 的API设计非常简单易用