vlambda博客
学习文章列表

一种redis lua 限流方式的简单分析

redis lua 限流有很多种方式, 本文分析一种网上最简单的最常见的方式。lua 脚本如下:

    --获取KEY
local key1
= KEYS[1]

--获取参数
local avg1
= ARGV[1]
local avg2
= ARGV[2]

--打印日志到reids
--注意,这里的打印日志级别,需要和redis.conf配置文件中的日志设置级别一致才行
redis.log(redis.LOG_WARNING,
"key1=" ..key1)
redis.log(redis.LOG_WARNING,
"avg=" ..avg1, avg2)

--将参数String转为数字类型
--限流时间窗
local expire
= tonumber(ARGV[1])
redis.log(redis.LOG_WARNING,
"时间窗=" ..expire)

--限流阈值
local limit
= tonumber(ARGV[2])
redis.log(redis.LOG_WARNING,
"限流频次=" ..limit)

--当前并发数
local current
= tonumber(redis.call('get', key1) or "0")
redis.log(redis.LOG_WARNING,
"当前并发=" ..current)

if current + 1 > limit then
     -- redis.call("INCRBY", key,"1") -- 如果不需要统计真是访问量可以不加这行
  
return 0
else
  redis.call(
"INCRBY", key1, "1")
  redis.call(
"expire", key1, expire)
  
return 1
end

这个脚本的作用是什么呢? 大意其实不难理解。

 


我的理解是一个累计计数器, 只要累计计数没有达到 limit,那么就允许流量,同时,如果某个操作之后空闲超过 窗口(即过期时间),那么重新计数。

 

redis.call("expire", key1, expire) 是关键一句,这个命令的作用是刷新过期时间, 相当于是延长key 的生命周期;空闲超过指定值,则重新计数。。

 

如果 窗口内 累计计数达到 limit,那么能否继续请求呢?不会,它直接返回0, 需要等待窗口结束(即过期之后)才可以继续接受流量请求。

 

此种方式限流, 有点像是滑动窗口, 不允许突发流量。虽然简单,但如果长时间的流量累计达到了limit ,也要被限流,感觉就不太好。比如 窗口是10s,limit 是100,我每9s发送一个请求,那么会被限流吗?会的,测试发现只有达到了 limit 就会。计算一下就是 9 * 100 s = 15min 。

 

就是说,虽然非常低的流量速率,但是仍然会被限流,所以感觉不是很实用,某些时候还是需要 令牌桶。


网上大量流传,都不知道他们有没有分析过,然后就简单复制拿过来,真可恶。