redis限速器设计(不使用lua脚本及事物)
时间:2021-07-01 10:21:17
帮助过:40人阅读
公司有一个需求,需要集群中的机器每分钟发送固定个数请求到局域网以外的一台服务器,固定个数,是指集群中的所有机器发送的所有的请求加起来是一个固定的个数,这就需要一个分布式的限速器。 首先想到的就是使用redis中的incr方法,在redis的官方文档中寻找
公司有一个需求,需要集群中的机器每分钟发送固定个数请求到局域网以外的一台服务器,固定个数,是指集群中的所有机器发送的所有的请求加起来是一个固定的个数,这就需要一个分布式的限速器。
首先想到的就是使用redis中的incr方法,在redis的官方文档中寻找到了一个示例
FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME()
keyname = ip+":"+ts
current = GET(keyname)
IF current != NULL AND current > 10 THEN
ERROR "too many requests per second"
END
IF current == NULL THEN
MULTI
INCR(keyname, 1)
EXPIRE(keyname, 1)
EXEC
ELSE
INCR(keyname, 1)
END
PERFORM_API_CALL()
但这个脚本对我来说有两个缺点 1 我的限速器并不是限制一台机器的速度,而是限制整个集群的速度,所以在判断了限速的key的长度之后,会有很多的机器执行incr动作 2 公司DBA不支持使用事物
看了文档中后面限速器的实现,要么使用事物,要么使用lua脚本(公司也不支持lua,因为lua也是事务性的,而公司是sharding的redis集群),全部被否定
看来只能多检测一会了程序流程图,假设我们1s中之内只能请求20次
步骤 1 使用incr命令得到一个值 2 判断值是否为1,为1则说明此次获得是key值失效之后第一次进行incr操作,这次操作需要将key设置超时时间。但是我们这里并没有使用事务支持,当程序运行到检测value是1,还没有进行expire操作的时候,机器down机了,那么程序就只能请求20次了,所以在每次得到value值之后,与10进行取余操作,若是10的倍数,则进行检测,如果没有设置超时时间,则进行设置 3 判断value值是否是小于20,如果小于等于20,我们认为在规定的时间内,这个线程获得了锁,如果大于20,我们认为没有获得锁,sleep一段时间后继续请求锁。
这样,我们就实现了一个没有使用事物,lua脚本的限速器
如果此方案有什么错误,或者有更加优雅的方案,还请各位多多指教。
注意流程图中值为1和为10的倍数都需要检测ttl key,这里可以做个优化,只有10的倍数的值的进行ttl key 检测操作,1的话直接expire key。