高并发-限流削峰

1.计数器限流 (lua脚本版)

<?php

$redis = getRedis();

// KEYS[1] redis的key, ARGV[1] 时间范围, ARGV[2] 限制次数,
$lua = <<<SCRIPT
                -- KEYS[1]为空不通过
                if (not KEYS[1]) 
                then
                    return false
                end
                
                -- KEYS[1] 自增1
                local incr = redis.call('INCR', KEYS[1])
                
                -- redis异常时直接通过
                if (incr == false)
                then
                    return true
                end
                
                if (incr > tonumber(ARGV[2])) 
                then
                    -- 如果大于最大限制次数,判断KEYS[1]的生命周期是否正常
                    local ttl = redis.call('TTL', KEYS[1])
                    if (ttl == -1)
                    then
                        -- 如果还未设置,则给KEYS[1]设置生命周期为ARGV[1]
                        redis.call('EXPIRE', KEYS[1], ARGV[1])
                    end
                    return false
                else
                    if (incr == 1)
                    then
                        -- 第一次执行KEYS[1],给KEYS[1]设置生命周期为ARGV[1]
                        redis.call('EXPIRE', KEYS[1], ARGV[1])
                    end
                    return true
                end
SCRIPT;

//给key1限流5秒10次
// EVAL 命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)
//$s = $redis->eval($lua, array('key1', 5, 10),1);

/**
 * 可以使用 EVALSHA 来代替 EVAL 
 * 使用 EVALSHA 命令对脚本进行复用,免去了无谓的带宽消耗
 */

//SCRIPT LOAD :将一个脚本装入脚本缓存,但并不立即运行它
//(可将缓存的值保存在redis里,以后每次只在redis里取出SHA1 校验和)
$rtn = $redis->script('load', $lua);
//给key1限流5秒10次
$s = $redis->evalSha($rtn, ['key1', 5, 10], 1);
var_dump($s, $redis->getLastError());



function getRedis()
{
    try{
        $redis = new Redis();
        $redis->connect('127.0.0.1', 6379, 60);
        return $redis;
    } catch (\Exception $e) {
        echo json(-1,'redis链接失败,请稍后再试');
    }
}


版权声明:本文为muwenbofx原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。