关于令牌桶( token bucket )
令牌桶限流的原理是系统以一个恒定的速度往固定容量的桶里放入令牌,当有请求进来时,需要先从桶里获取并消耗一个令牌,当桶里没有令牌可取时,则拒绝服务或让请求等待。
如图:
每隔 1/r 秒向 bucket 中填充一个 token ;
bucket 最多只能存放 b 个 token ,如果填充 token 时 bucket 已经满了,这丢弃这个 token ;
当请求到达时,从 bucket 获取并消耗一个 token ,并处理请求;
如果 bucket 中的 token 数不足,则不消耗 token ,直接拒绝处理本次请求。
在取 token 时可以通过计算上次取跟这次取之间按照速率会产生多少个 token 加上上次剩余的 token ,然后比较剩余 token 数来替代使用一个线程支持在后台持续更新 token 的方案来避免性能问题。
参考文章:
代码地址
关于实现
在采用 Nginx + Lua 实现限流方案时,利用 Redis 的 EVAL 命令的原子性来保证令牌桶相关运算逻辑在对 token 进行加减计算时的并发安全。
Redis 的 EVAL 命令可以执行 Lua 脚本内容,将需要执行的 Lua 代码以字符串内容的形式传入,并传入脚本内容需要的相关参数, Redis 会使用单个 Lua 解释器去运行所有脚本,并且 Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Red