Skip to content

限流

介绍

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

lightbulb

如果您对限流传入的 HTTP 请求感兴趣,请查阅 限流中间件文档

缓存配置

通常,限流器利用您应用程序的默认缓存,如您应用程序的 cache 配置文件中的 default 键所定义。然而,您可以通过在应用程序的 cache 配置文件中定义 limiter 键来指定限流器应使用哪个缓存驱动:

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

'limiter' => 'redis',

基本用法

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

attempt 方法在回调没有剩余尝试时返回 false;否则,attempt 方法将返回回调的结果或 trueattempt 方法接受的第一个参数是限流器的“键”,可以是您选择的任何字符串,表示正在限流的操作:

php
use Illuminate\Support\Facades\RateLimiter;

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

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

如果需要,您可以向 attempt 方法提供第四个参数,即“衰减率”,或可用尝试重置的秒数。例如,我们可以修改上面的示例,以允许每两分钟五次尝试:

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 '尝试次数过多!';
}

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);

确定限流器可用性

当一个键没有更多的尝试时,availableIn 方法返回在更多尝试可用之前剩余的秒数:

php
use Illuminate\Support\Facades\RateLimiter;

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

    return '您可以在 '.$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;
}