前言
上文粗略的讲了限流和熔断的能解决什么问题,使用场景是什么。本文主要讲述 RateLimiter 插件的配置使用和解析,以单体 redis 为例进行讲解。 根据官网文档来看, RateLimiter 是基于令牌桶实现的。
操作流程
配置
1、 进入 admin 控制台,在菜单 "System Manage => Plugin" 中配置 rate_limiter
。操作如下图所示:
这里 redis 的配置是需要注意的,当这里配置错误时,或者 redis 没有启动时,虽然会匹配到 RateLimiter 插件,但实际是不起作用的。
-
增加 Seletor 和 Rule, 步骤如下图所示:
增加 Seletor:
增加 Rule
Handler 中有两个参数:
- capacity: 令牌桶的容量。
- rate: 令牌的刷新速率,即每秒产生多少个令牌。
验证配置
- 启动
soul-examples
中的soul-examples-http
。 -
启动 postman, 利用 runner 查看请求情况。
增加测试
启动 runner
-
设置并发数 (Iterations)为 50,
源码解析
admin 数据发生变更
- 当插件信息发生变更时,BootStrap 中接口 PluginDataSubscriber 有订阅到了信息的变更,它的实现 CommonPluginDataSubscriber, 由该类中 subscribeDataHandler 的方法,进入到 RateLimiterPluginDataHandler 逻辑中。
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
if (data instanceof PluginData) {
PluginData pluginData = (PluginData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cachePluginData(pluginData);
// pluginData.getName() == rate_limite
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
}
...
}
...
});
}
- 进入 RateLimiterPluginDataHandler 的 handlerPlugin 中,看到 createLettuceConnectionFactory 方法,进入该方法,发现这里是根据之前 admin 的配置生成相应的 LettuceConnectionFactory 的连接池,由于之前是 单机模式, 则这里就采用了
redisStandaloneConfiguration
。
public void handlerPlugin(final PluginData pluginData) {
if (Objects.nonNull(pluginData) && pluginData.getEnabled()) {
...
//spring data redisTemplate
if (Objects.isNull(Singleton.INST.get(ReactiveRedisTemplate.class))
|| Objects.isNull(Singleton.INST.get(RateLimiterConfig.class))
|| !rateLimiterConfig.equals(Singleton.INST.get(RateLimiterConfig.class))) {
LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(rateLimiterConfig);
...
// 创建 reactiveRedisTemplate, 模板中加载了 redis 的 lua 脚本
ReactiveRedisTemplate<String, String> reactiveRedisTemplate = new SoulReactiveRedisTemplate<>(lettuceConnectionFactory, serializationContext);
Singleton.INST.single(ReactiveRedisTemplate.class, reactiveRedisTemplate);
Singleton.INST.single(RateLimiterConfig.class, rateLimiterConfig);
}
}
}
访问相应链接
- 当访问链接
localhost:9195/http/test/findByUserId?userId=1
时,从终端的日志看到
2021-02-04 17:26:23.341 INFO 12552 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : rate_limiter selector success match , selector name :测试1
2021-02-04 17:26:23.343 INFO 12552 --- [-work-threads-1] o.d.soul.plugin.base.AbstractSoulPlugin : rate_limiter rule success match , rule name :test22222
由此我们可知进入到了 RateLimiterPlugin 中。 查看 doExecute
方法中有 isAllowed 的判断,而 isAllowed 中利用 ReactiveRedisTemplate 执行 lua 脚本实现的令牌桶,来判断。
总结
- RateLimiter 利用了 Redis 单线程的处理线程,实现了令牌桶算法,保证数据的原子性。