分享個自己封裝的限流trait

王大牛發表於2023-05-05
<?php

namespace App\Traits;

use App\Exceptions\BusinessException;
use Illuminate\Support\Facades\Redis;

trait RedisLimiter
{
    /**
     * 限流
     * @param $key
     * @param $limit
     * @param $expire_time
     * @return bool
     * @throws BusinessException
     * @author wzx 2023/4/25 17:35
     */
    public static function set($key, $limit, $expire_time)
    {
        $current_time = time();

        $result = Redis::eval(self::lua(), 1, $key, $limit, $expire_time, $current_time);

        if ($result == 0) {
            throw new BusinessException([-1, '請求頻繁']);
        }

        return true;
    }

    private static function lua(): string
    {
        return <<<LUA
        local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = tonumber(ARGV[2])
local current_time = tonumber(ARGV[3])

local count = redis.call('get', key)
if count and tonumber(count) >= limit then
    return 0
else
    redis.call('incr', key)
    redis.call('expire', key, expire_time)
    return 1
end
LUA;
    }
}

使用

$key = "user:xxx" . $user_id;
// 一分鐘請求1次
RedisLimiter::set($key, 1, 60);
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章