local key = KEYS[1] -- 限频的key local consume_permit = tonumber(ARGV[1]) -- 所取走的令牌数 local curr_time = tonumber(ARGV[2]) -- 当前时间,毫秒 local max_wait = tonumber(ARGV[3]) -- 最大等待时间,毫秒 local bucket_cap = tonumber(ARGV[4]) -- 令牌桶容量 local period = tonumber(ARGV[5]) -- 周期,毫秒。即假若桶是空的,经过一个周期,桶就能被填满 local rate = bucket_cap / period -- 速率,每毫秒生成的令牌数量
-- key的数据结构是hash,如有,保存着上次取令牌的时间和剩余令牌数 local limit = redis.pcall("HMGET", key, "last_time", "curr_permit") local last_time = tonumber(limit[1]) or0 local curr_permit = tonumber(limit[2]) or0
-- 计算当前令牌数量 local new_permit = math.floor((curr_time-last_time)*rate) -- 上次取令牌到现在为止,新增了多少令牌 local total_permit = curr_permit + new_permit -- 现在桶里有多少令牌 if bucket_cap < total_permit then total_permit = bucket_cap end
-- 延迟时间判断 local wait_time = 0 if consume_permit <= total_permit then -- 如果当前桶里牌数大于要取走的牌数,直接取就好 total_permit = total_permit - consume_permit redis.pcall("HMSET", key, "curr_permit", total_permit, "last_time", curr_time) else wait_time = (consume_permit-total_permit)/rate -- 需要多久才能生成这些透支的令牌 if max_wait < wait_time then wait_time = -wait_time -- 返回<0,代表超出延迟时间,被拦截 else total_permit = total_permit - consume_permit redis.pcall("HMSET", key, "curr_permit", total_permit, "last_time", curr_time) end end