Skip to content

速率限制

介绍

Laravel 包含一个简单易用的速率限制抽象,它与应用程序的缓存相结合,提供了一种在指定时间窗口内限制任何操作的简单方法。

NOTE

如果您对对传入的 HTTP 请求进行速率限制感兴趣,请查阅速率限制器中间件文档

缓存配置

通常,速率限制器使用应用程序cache配置文件中 default 键定义的默认应用程序缓存。但是,您可以通过在应用程序的cache配置文件中定义 limiter 键来指定速率限制器应使用哪个缓存驱动程序:

php
    'default' => env('CACHE_STORE', 'database'),

    'limiter' => 'redis',

基本用法

Illuminate\Support\Facades\RateLimiter Facade 可用于与速率限制器交互。速率限制器提供的最简单方法是 attempt 方法,该方法在给定的秒数内对给定回调进行速率限制。

当回调没有剩余的尝试时,attempt 方法返回 false;否则,attempt方法将返回回调的 result 或 trueattempt方法接受的第一个参数是速率限制器 “key”,它可以是您选择的任何字符串,表示被速率限制的操作:

php
    use Illuminate\Support\Facades\RateLimiter;

    $executed = RateLimiter::attempt(
        'send-message:'.$user->id,
        //每分钟尝试5次
        $perMinute = 5,
        function() {
            // 发送消息...
        }
    );

    if (! $executed) {
      return '发送的消息太多了!';
    }

如有必要,您可以为 attempt 方法提供第四个参数,即 “decay rate” 或重置可用尝试之前的秒数。例如,我们可以修改上面的示例,允许每两分钟尝试 5 次:

php
    $executed = RateLimiter::attempt(
        'send-message:'.$user->id,
        $perTwoMinutes = 5,
        function() {
            // 发送消息...
        },
        $decayRate = 120,
    );

手动递增尝试次数

如果要手动与速率限制器交互,可以使用各种其他方法。例如,您可以调用 tooManyAttempts 方法来确定给定的速率限制器密钥是否超过了每分钟允许的最大尝试次数:

php
    use Illuminate\Support\Facades\RateLimiter;

    if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
        return 'Too many attempts!';
    }

    RateLimiter::increment('send-message:'.$user->id);

    // 发送消息...

或者,您可以使用 remaining 方法来检索给定密钥的剩余尝试次数。如果给定的键有剩余的重试次数,您可以调用 increment 方法来增加总尝试次数:

php
    use Illuminate\Support\Facades\RateLimiter;

    if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {
        RateLimiter::increment('send-message:'.$user->id);

        // 发送消息...
    }

如果要将给定速率限制器键的值增加多个,则可以向 increment 方法提供所需的数量:

php
    RateLimiter::increment('send-message:'.$user->id, amount: 5);

确定 Limiter 可用性

当键没有更多尝试时, availableIn 方法返回剩余秒数,直到有更多尝试可用:

php
    use Illuminate\Support\Facades\RateLimiter;

    if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
        $seconds = RateLimiter::availableIn('send-message:'.$user->id);

        return 'You may try again in '.$seconds.' seconds.';
    }

    RateLimiter::increment('send-message:'.$user->id);

    // 发送消息...

清除尝试

您可以使用 clear 方法重置给定速率限制器密钥的尝试次数。例如,当接收方读取给定消息时,您可以重置尝试次数:

php
    use App\Models\Message;
    use Illuminate\Support\Facades\RateLimiter;

    /**
     * 将消息标记为已读。
     */
    public function read(Message $message): Message
    {
        $message->markAsRead();

        RateLimiter::clear('send-message:'.$message->user_id);

        return $message;
    }