一、Soul-Admin的相关配置
- 开启resilience4j
运行Soul-admin,进入管理界面:系统管理 --> 插件管理 --> resilience4j ,点击编辑,把它开启
- 配置选择器与规则
选择器参照divide的匹配方式,让符合条件的http的请求,能被捕获到
规则配置,配置项名称官网的文档对应不太一样,一下按照自己的理解解释下:
token filling period (ms):刷新令牌的时间间隔,单位ms,默认值:500。
token filling number:每次刷新令牌的数量,默认值:50。
control behavior timeout (ms):熔断服务,对外停止服务持续时间,单位ms
circuit enable:是否开启熔断,0:关闭,1:开启,默认值:0。
circuit timeout (ms):熔断超时时间,请求服务响应超过此时间,则触发熔断,单位ms,默认值:30000。
fallback uri:降级处理的uri。
sliding window size:滑动窗口大小,默认值:100。
sliding window type:滑动窗口类型,0:基于计数,1:基于时间,默认值:0。
enabled error minimum calculation threshold: 开启熔断的最小请求数,超过这个请求数才开启熔断统计,默认值:100。
-
degrade opening duration:熔断器开启持续时间,单位ms,默认值:10
此配置请一定大于或等于1000,小心有坑
half open threshold:半开状态下的环形缓冲区大小,必须达到此数量才会计算失败率,默认值:10。
degrade failure rate:错误率百分比,达到这个阈值,熔断器才会开启,默认值50。
二、Soul-Bootstrap配置启动
org.dromara
soul-spring-boot-starter-plugin-resilience4j ${last.version}
三、测试
真正测试之前要明确一下关于熔断状态项
三个一般性状态
- CLOSED:关闭状态,放过所有请求,记录请求状态。
- OPEN:打开,异常请求达到阀值数量时,开启熔断,拒绝所有请求。
- HALF_OPEN:半开,放开一定数量的请求,重新计算错误率。
两个待定状态
- DISABLED:禁用
- FORCED_OPEN:强开
一图胜千言
因为熔断需要达到一定的阈值才能触发,所以跑一个wrk,让流量压起来
wrk -t4 -c32 -d10s http://localhost:9195/http/test/findByUserId?userId=2
启动测试一下::http://localhost:9195/http/test/findByUserId?userId=2
此处有一点小问题:明显看出此时熔断已经起作用了,但是配置了fallback uri,转发请求失败,需要调试一下
{
"code": 500,
"message": "Internal Server Error",
"data": "404 NOT_FOUND \"No matching handler\""
}
熔断周期结束后,流量降下来了,同时单个响应时间也降下来时,就返回正常了
{
"userId": "2",
"userName": "hello world"
}
四、源码分析
RateLimiterPlugin是继承了AbstractSoulPlugin,所以会先走AbstractSoulPlugin中路由匹配的相关逻辑,之后再走RateLimiterPlugin的doExcute限流的处理逻辑,以上都其他插件的流程是类似的。
# AbstractSoulPlugin
// 首先进行路由匹配
public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named(); final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); if (pluginData != null && pluginData.getEnabled()) {
// some code
return doExecute(exchange, chain, selectorData, rule);
}
return chain.execute(exchange);
}
进入RateLimiterPlugin看看具体作了什么工作
# RateLimiterPlugin
// 匹配完成后走限流的逻辑
protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
// 此处soul-admin配置规则转json转成对象,注意格式,不能瞎整
Resilience4JHandle resilience4JHandle = GsonUtils.getGson().fromJson(rule.getHandle(), Resilience4JHandle.class);
// 只有Circle enable不等于1时,走限流的逻辑,其他都是熔断
if (resilience4JHandle.getCircuitEnable() == 1) {
return combined(exchange, chain, rule);
}
return rateLimiter(exchange, chain, rule);
}
// 给CombinedExecutor父类Executor的高阶函数run,传递设置熔断中各种情况的处理方式,以及生成熔断器等方法处理方式
private Mono combined(final ServerWebExchange exchange, final SoulPluginChain chain, final RuleData rule) {
Resilience4JConf conf = Resilience4JBuilder.build(rule);
return combinedExecutor.run(
// chain.execute(exchange)作为Mono类型参数,传递到run方法中,用于触发下一个插件
chain.execute(exchange).doOnSuccess(v -> {
// 请求失败,抛出封装的熔断异常
if (exchange.getResponse().getStatusCode() != HttpStatus.OK) {
HttpStatus status = exchange.getResponse().getStatusCode();
exchange.getResponse().setStatusCode(null);
throw new CircuitBreakerStatusCodeException(status);
}
// Resilience4JPlugin的fallback,用转发熔断请求
}), fallback(combinedExecutor, exchange, conf.getFallBackUri()), conf);
}
进入combinedExecutor,再探究竟,主要是以 Webflux 中Mono对象,对请求的各种处理方式设置,以及熔断转发的触发
if (fallback != null) {
to = to.onErrorResume(fallback);
}
总结:
1.这个插件熔断转发没有测试成功,目测应该是响应式编程的知识欠缺
2.代码写不好需要重构,文章也一样
3.日拱一卒