美文网首页工作生活
分布式限流 - 基于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