造轮子的过程也是学习的过程。
如果公司的 Redis 不支持发布订阅指令的话,是没法用 Redisson 的,因为 Redisson 的大部分功能都依赖于 Redis 的发布订阅指令。
这是完整实现的代码仓库:https://gitee.com/wu0916/redis-rate-limiter
下面这段 lua 代码是基于 Redis 的 RateLimiter 的实现:
local maxToken = tonumber(ARGV[1]) -- 单位时间内的最大 token 数
local intervalTime = tonumber(ARGV[2]) -- 生成一个 token 的间隔毫秒数
local curTime = tonumber(ARGV[3]) -- 当前时间的毫秒数
local remainToken -- 当前可用的 token 数
local latestTime = redis.call("HGET", KEYS[1], "latestTime") -- 上次生成 token 的时间
-- 将当前时间对齐到 intervalTime
-- 比如 intervalTime 是 200,当前时间是 1662444699797,则对齐后的时间就是 1662444699600
local alignTime = curTime - (curTime % intervalTime)
if latestTime then
-- 获取剩余可以用 token 数
remainToken = tonumber(redis.call("HGET", KEYS[1], "remainToken"))
-- 计算与上次生成 token 的时间差,是否允许再次生成一个 token
local diffTime = alignTime - tonumber(latestTime)
-- 与上次生成时间相差不到 1秒
if diffTime < 1000 then
-- 计算可以生成几个 token
local newTokenNum = math.floor(diffTime / intervalTime)
-- 将新生成的 token 累加到剩余 token 中,然后更新生成 token 的时间
if newTokenNum > 0 then
remainToken = remainToken + newTokenNum
redis.call("HSET", KEYS[1], "latestTime", alignTime)
end
elseif diffTime == 1000 then
-- 新的一秒的开始
remainToken = 1
redis.call("HSET", KEYS[1], "latestTime", alignTime)
else
-- 计算到整秒时间戳的差
diffTime = alignTime - (curTime - (curTime % 1000))
local newTokenNum = math.floor(diffTime / intervalTime)
if newTokenNum > 0 then
remainToken = remainToken + newTokenNum
redis.call("HSET", KEYS[1], "latestTime", alignTime)
end
end
else
-- 第一次获取,先放入一个 token,然后初始化刷新时间
remainToken = 1
redis.call("HSET", KEYS[1], "latestTime", alignTime)
end
if remainToken <= 0 then
redis.call("HSET", KEYS[1], "remainToken", 0)
return 0
else
redis.call("HSET", KEYS[1], "remainToken", remainToken - 1)
return 1
end
有任何问题欢迎提出。
标签:redis,RateLimiter,--,latestTime,Redis,remainToken,token,轮子,local From: https://www.cnblogs.com/wuqinglong/p/16661930.html