美文网首页工作生活
分布式限流 - 基于redis

分布式限流 - 基于redis

作者: 沐兮_d64c | 来源:发表于2019-07-06 23:48 被阅读0次

1,基于redis计数器

1)普通redis incr限流。不能保证原子性

image.png
2)lua脚本实现计数器限流。redis保证同时只有一条lua脚本执行,保证原子性。
执行lua脚本eval inc_rate_limit_script 3 query_data_limit_key 10 1000 1
image.png

2,基于令牌桶算法限流

1)lua脚本实现令牌桶限流


local key = KEYS[1] --作为锁定的key,hash类型
local max_permits = tonumber(KEYS[2]) --最大令牌数
local permits_per_second = tonumber(KEYS[3]) --每秒产生的令牌数
local req_permits = tonumber(ARGV[1]) --请求的令牌数

-- 下次请求可以获取令牌的开始时间
local next_free_ticket_micros = tonumber(redis.call('hget', key, 'next_free_ticket_micros') or 0)
local time = redis.call('time');
local now_micros = tonumber(time[1]) * 1000000 + tonumber(time[2])

-- 查询获取令牌是否超时
if(ARGV[2] ~= nil) then
    --获取令牌的超时时间,第二个输入参数,微秒
    local timeout_micros = tonumber(ARGV[2])
    local micros_to_wait = next_free_ticket_micros - now_micros
    if(micros_to_wait > timeout_micros) then
        return micros_to_wait
    end
end

-- 存储的令牌数
local stored_permis = tonumber(redis.call('hget', key, 'stored_permis') or 0)
--添加令牌的间隔微秒
local stable_interval = 1000000 / permits_per_second

-- 如果当前请求时间 大约保存的nextFreeTicketTime,则进行令牌补充
if(now_micros > next_free_ticket_micros) then
    local new_permits = (now_micros - next_free_ticket_micros) / stable_interval
    --更新存储的令牌数
    stored_permis = math.min(max_permits, stored_permis + new_permits)
    next_free_ticket_micros = now_micros
end

-- 获取令牌
local snapshot_time = next_free_ticket_micros
-- 本次可以花费的令牌
local permits_to_spend = math.min(req_permits, stored_permis)
-- 还差多少令牌不够
local fresh_permits = req_permits - permits_per_second
--需要等待的时间
local wait_micros = fresh_permits * stable_interval

redis.replicate_commands()--包装成MULTI/EXEC事务,发送给AOF或者从库
redis.call('hset', key, 'stored_permis', stored_permis - permits_to_spend)
redis.call('hset', key, 'next_free_ticket_micros', next_free_ticket_micros + wait_micros)
redis.call('expire', key, 10) --设置10s过期,有流量的情况下不会过期

return snapshot_time - now_micros;

2)调用逻辑
acquire获取:eval 'lua脚本' 3 '自定义的key' '最大存储的令牌数' '每秒钟产生的令牌数' '请求的令牌数'
eg:eval 'lua脚本' 3 my_limit_key 100 100 1,返回线程需要等待的微秒数。
tryAcquire获取:eval 'lua脚本' 3 '自定义的key' '最大存储的令牌数' '每秒钟产生的令牌数' '请求的令牌数' '最大等待的微秒数'
eg:eval 'lua脚本' 3 my_limit_key 100 100 1 10000 返回线程需要等待的微秒,与req的timeout对比。

相关文章

网友评论

    本文标题:分布式限流 - 基于redis

    本文链接:https://www.haomeiwen.com/subject/yrwthctx.html