package redis
const (
// BucketScript 首先根据key从redis中获取相关的令牌桶配置(先获取一次,如果没有值的话就初始化),
// 然后根据传入的当前时间去计算上次获取令牌到这次中间产生了多少个令牌
//(如果产生的令牌数加上剩余的超过令牌桶的容量的话,选择让剩余的令牌数量等于容量)
// 然后更新redis中的相关的值。
// 然后如果剩余的令牌数大于零的话,就通过此次的请求,让令牌数减一并更新redis,否则不通过。
BucketScript = `
-- 令牌桶限流算法
-- bucket 令牌桶名称
-- size 桶的大小
-- interval 单位秒
local function alloc_token(bucket, size, interval)
redis.replicate_commands()
-- 获取当前时间戳
local now = redis.call('TIME')[1]
-- 获取令牌桶的信息
local rate_limit_info = redis.call('HMGET', bucket, 'last_time', 'tokens')
local last_time = tonumber(rate_limit_info[1]) or 0
local tokens = tonumber(rate_limit_info[2]) or size
-- 计算时间间隔内新增的令牌数
local elapsed = now - last_time
local refill = math.floor(elapsed * size / interval)
-- 重新填充令牌桶
if refill > 0 then
tokens = math.min(tokens + refill, size)
redis.call('HSET', bucket, 'tokens', tokens)
redis.call('HSET', bucket, 'last_time', now)
end
-- 检查令牌是否足够
if tokens > 0 then
tokens = tokens - 1
redis.call('HSET', bucket, 'tokens', tokens)
return true
else
return false
end
end
-- 调用限流函数
return alloc_token(KEYS[1], ARGV[1], ARGV[2])
`
)
// AllocBucketToken 从令牌桶申请令牌
func AllocBucketToken(bucket string, size int, interval int) bool {
result, err := redis.RunScript(BucketScript, []string{bucket}, size, interval)
if err != nil {
//logs.Errorf("AllocBucketToken err: %+v", err)
return false
}
if result == int64(1) {
return true
}
return false
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |