soul源码学习(十六)-限流插件之rate_limiter(下)

常用限流算法梳理

  1. 计数器(固定窗口)算法

    计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。

    计数器算法方式限流对于周期比较长的限流,存在很大的弊端,会出现临界问题(前一个周期最后的小时间段内和下一个周期的最开始时间段内相加的并发量远远大于服务器可承载并发数)。

  2. 滑动窗口算法

    滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。

    滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

  3. 漏桶算法

    漏桶算法是访问请求到达时直接放入漏桶,如当前容量已达到上限(限流值),则进行丢弃(触发限流策略)。漏桶以固定的速率进行释放访问请求(即请求通过),直到漏桶为空。

  4. 令牌桶算法

    令牌桶算法是程序以r(r=时间周期/限流值)的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略

令牌桶算法原理

令牌桶算法主要用来控制发送到网络上的数据的数目,并允许突发数据的发送。大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

令牌桶算法优势

  1. 限制发送速率
  2. 由于令牌桶算法存在固定容量的特性,因此可以应对突发流量

令牌桶算法实现

在rate_limiter插件中,执行限流的关键数据是RateLimiterPlugin类中的doExecute方法
在这里插入图片描述
根据调用方法名称可以知道其使用了redis进行限流实现,关键实现类在RedisRateLimiter该类中,
在这里插入图片描述
在这里插入图片描述
可以看到其最终调用的是一段lua脚本,其中具体内容为:

local tokens_key = KEYS[1] //令牌桶key
local timestamp_key = KEYS[2]//时间戳key

local rate = tonumber(ARGV[1]) //填充速率
local capacity = tonumber(ARGV[2])//令牌桶容量
local now = tonumber(ARGV[3])//当前调用事件
local requested = tonumber(ARGV[4])//请求个数,默认为1

local fill_time = capacity/rate //填满整个桶需要的时间
local ttl = math.floor(fill_time*2)//计算key过期时间

local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then
  last_tokens = capacity
end //获取上一次令牌桶的剩余大小

local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil then
  last_refreshed = 0
end //获取上一次执行的时间戳

local delta = math.max(0, now-last_refreshed) //计算本次与上一次调用的时间间隔
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))//该段代码非常重要,用来计算本次需要填充的令牌数
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
  new_tokens = filled_tokens - requested//将本次的消耗数减去,算出整正需要填充的数量
  allowed_num = 1
end

redis.call("setex", tokens_key, ttl, new_tokens)
redis.call("setex", timestamp_key, ttl, now)

return { allowed_num, new_tokens }

因此可以得出整个令牌桶算法的计算过程

  1. 获取上次令牌桶的大小和执行时间戳
  2. 通过传入的现在的时间戳以及与上一次的时间戳算出时间间隔X填充速率,算出填充个数,与桶大小取两者最小
  3. 比较填充的令牌数与当前请求的令牌数,如果前者小于后者,证明令牌已用完,需要限流,如果前者大于后者,证明桶内令牌数充足,允许放行,并将填充的令牌数-请求的令牌数,算出最终桶内的令牌数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值