在redis中使用lua脚本,使用脚本完成限流操作

redis支持lua脚本,可以在lua脚本中将多个redis执行单元组合在一起,完成原子性操作。先来看一个使用lua的简单示例:

eval "if redis.call('get',KEYS[1]) == ARGV[1] then return 0 else return -1 end" 1 name star

基本语法就是这样,KEYS用来传递redis要使用key值,ARGV用来传递脚本需要的值参数(你要倒行逆施用ARGV传key值,KEYS当作value也是ok的),只是最后要指定KEYS参数个数,紧跟着KEYS参数,最后跟着ARGV参数。

搞清楚了基础语法,就可以使用redis命令组合来完成一个限流操作了,类似数据库存储过程,在jedis中使用和在redis-cli中使用是一致的。

我们先写一个简单计数器限流,未指定的限流key设置过期时间(单位限流时间),超过限流值时,返回失败。

local curr_count = tonumber(redis.call('incr',KEYS[1]))
if curr_count == 1 then 
	redis.call('pexpire',KEYS[1],tonumber(ARGV[1]))
elseif curr_count > tonumber(ARGV[2]) then 
	return -1
end
return curr_count 

可以将脚本加载到redis,通过sha值调用脚本。

src/redis-cli -h 127.0.0.1 -a redis5 script load "$(cat lua-scripts/count_limit.lua)"
#得到sha值
evalsha $sha  1  day  2000  1 

2000毫秒内,限流一个,可以执行看到效果,两秒内多次执行返回-1。实现起来简单,但限流不够精准,会出现很多极端情况。比如两个时间窗口临界处出现2倍的请求,第一个2秒内最后一毫秒出现一个请求,第二个2秒内的第一个毫秒出现一个请求等等。

所以时间粒度应尽量细一些,比如每秒1000请求,可以细分为100毫秒100个,限流就是为了保护应用不被拖垮。

下面使用令牌桶算法实现限流,令牌桶其实是类似计数算法的,基本思想就是:先初始化一个满的令牌桶(数量是有上限的);每个请求来了都要去令牌桶获取令牌(令牌消耗);令牌桶以一定的间隔周期重新生成令牌放入桶中。

local capacity = tonumber(ARGV[1])
local now = tonumber(ARGV[2])
local rate = tonumber(ARGV[3])

local curr_tokens = redis.call('get',KEYS[1])
local ttl = capacity / rate * 2
local new_tokens = 0
if curr_tokens then
	local last_millis = redis.call('get',KEYS[1]..'_millis')
	local new_add = math.floor((now - last_millis) *  rate)
	new_tokens = math.min(capacity, curr_tokens + new_add) -1
	if new_tokens < 1 then
		return -1
	end
else
	new_tokens = capacity -1
end
redis.call('set',KEYS[1],new_tokens,'PX',ttl)
redis.call('set',KEYS[1]..'_millis',now,'PX',ttl)
return new_tokens

这里粗略使用毫秒作为新生成速率时间单位,rate代表每毫秒生成多少个,capacity表示桶的最大容量,now代表本次请求的时间毫秒数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小麦

一起学习共同进步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值