今天因为工作需要,查看了下redis的文档,这里简单总结下看到的知识。
改篇文章可以算做这个的读后感:https://redis.io/commands/incr
需求是:每秒
中某一个Ip最多允许10次请求。
其中伪代码是:
FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME() //获取当前秒的时间戳
keyname = ip+":"+ts //key为 ip+ts
current = GET(keyname) //获取当前key是否存在
//细节1:最容易遇到的现象放在前面
IF current != NULL AND current > 10 THEN
ERROR "too many requests per second"
END
//细节2:只会遇到一次的情况放后面
IF current == NULL THEN
MULTI //redis中开启事务
INCR(keyname, 1) //增加1
EXPIRE(keyname, 1) //并且设置过期时间
EXEC //执行事务,要么执行完成,要么完全不执行
ELSE
INCR(keyname, 1)
END
PERFORM_API_CALL()
上述代码我添加了注释 //
后的就是注释代码!
上述细节中有几个事情需要了解
String
类型来装载的MULTI
和EXEC
要联合使用,其中的命令,要么一起成功,要么一起失败。ts
该如何处理了?yyyyMMddHHmm
也是可以做到,但是这个存在bug啊:某一分钟的前30秒执行的请求5,等这个某一分钟正式开始后,又可以执行10个请求了。IPV6
出来可能更好】伪代码如图
FUNCTION LIMIT_API_CALL(ip):
current = GET(ip)
IF current != NULL AND current > 10 THEN
ERROR "too many requests per second"
ELSE
value = INCR(ip)
IF value == 1 THEN
EXPIRE(ip,1)
END
PERFORM_API_CALL()
END
他们的判定,其实很类似,但是在细微处有差别,所以感慨一句:逻辑真的是一个很神奇的东西啊。
方法2
的设置过期时间失败了?那么这个Key
就会一直存在啊。local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],1)
end
lua脚本是原子性的,所以利用他来进行key的初始化,这样就不会有事务的问题。
上述代码是核心代码,继续弥补后如下所示:
local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],1)
end
if tonumber(current) > KEYS[2] //KEYS[2]是最大次数
return 1
else
return 0
lua
的脚本不太熟悉,上面的代码不一定能执行,意思就是这个,你如果需要用,那么就将其改造好即可。
其实和第一个的核心原理很像,第一个是利用STRING
的INCR
,这个利用LIST
的LLEN
个人感觉第一个对内存的占用更低,因为这个还存储的具体的值,而且值应该都是一样的,坑啊!!!
FUNCTION LIMIT_API_CALL(ip)
current = LLEN(ip)
IF current > 10 THEN
ERROR "too many requests per second"
ELSE
IF EXISTS(ip) == FALSE //通过EXISTS(IP)
MULTI //开启事务
RPUSH(ip,ip) //key为ip value也是ip 好逗!
EXPIRE(ip,1) //设置过去时间
EXEC
ELSE
RPUSHX(ip,ip) //key为ip value也是ip 好逗!
END
PERFORM_API_CALL()
END
谢谢观看!