一、为什么要限流
在大型互联网项目中,瞬时的请求量是成千上万的,但系统的处理能力往往存在临界阈值。当流量洪峰来临时,如何避免系统被击穿?实现方式有很多种,例如:令牌桶算法,漏桶算法、固定窗口算法、滑动窗口算法。这里我将梳理分布式系统中滑动窗口限流算法的实现,并基于Redisson的RRateLimiter给出可落地的解决方案。
二、为什么用滑动窗口算法
其他三种算法的痛点如下:
- 固定窗口:存在临界时刻双倍流量冲击问题
- 令牌桶算法:虽然允许突发流量,但预热机制复杂
- 漏桶算法:强制平滑流量,无法应对合理突发
而滑动窗口:
- 以时间片为单位滑动统计
- 精准控制单位时间内的请求量
- 有效解决传统算法的临界突变问题
滑动窗口算法有两个重要概念,即 请求速率 和 窗口大小,也就是在规定的窗口大小中,请求数量不能超过给定的请求速率值。
如何去实现呢,常见的实现方案即使用redis的zset去实现,通过将加入的请求的请求时间作为每个请求的score,进行排序,每次新的请求进入的时候,若请求数小于等于剩余可接收请求个数,那么则将请求记入zset。
Redisson的RRateLimiter通过lua脚本包装好了相关的算法,我们直接看它的实现。
三、Redisson RRateLimiter原理解析
直接看Redisson RRateLimiter 的lua脚本部分,这里参考RRateLimiter原理解析
-- 速率
local rate = redis.call("hget", KEYS[1], "rate")
-- 时间区间(ms)
local interval = redis.call("hget", KEYS[1], "interval")
local type = redis.call("hget", KEYS[1], "type")
assert(rate ~= false and interval ~= false and type ~= false, "RateLimiter is not initialized")
-- {name}:value 分析后面的代码,这个key记录的是当前令牌桶中的令牌数
local valueName = KEYS[2]
-- {name}:permits 这个key是一个zset,记录了请求的令牌数,score则为请求的时间戳
local permitsName = KEYS[4]
-- 单机限流才会用到,集群模式不用关注
if type == "1" then
valueName = KEYS[3]
permitsName = KEYS[5]
end
-- 原版本有bug(https://github.com/redisson/redisson/issues/3197),最新版将这行代码提前了
-- rate为1 arg1这里是 请求的令牌数量(默认是1)。rate必须比请求的令牌数大
assert(tonumber(rate) >= tonumber(ARGV[1])

最低0.47元/天 解锁文章
85万+

被折叠的 条评论
为什么被折叠?



