laravel
5. 深入探讨
速率限制

介绍

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

[!NOTE]
如果您对限制传入的 HTTP 请求的速率感兴趣,请查阅速率限制中间件文档

缓存配置

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

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

'limiter' => 'redis', 

基本用法

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

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

use Illuminate\Support\Facades\RateLimiter;
 
$executed = RateLimiter::attempt(
    'send-message:'.$user->id,
    $perMinute = 5,
    function() {
        // 发送消息...
    }
);
 
if (! $executed) {
  return '发送的消息过多!';
}

如有必要,您可以为 attempt 方法提供第四个参数,即“衰减率”,或者说是直到可用尝试次数重置的秒数。例如,我们可以修改上面的示例,以允许每两分钟进行五次尝试:

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

手动增加尝试次数

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

use Illuminate\Support\Facades\RateLimiter;
 
if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
    return '尝试次数过多!';
}
 
RateLimiter::increment('send-message:'.$user->id);
 
// 发送消息...

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

use Illuminate\Support\Facades\RateLimiter;
 
if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {
    RateLimiter::increment('send-message:'.$user->id);
 
    // 发送消息...
}

如果您想将给定速率限制器键的值增加多于一次,可以向 increment 方法提供所需的数量:

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

确定限制器可用性

当一个键没有更多尝试次数时,availableIn 方法会返回直到有更多尝试次数可用为止的剩余秒数:

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 方法重置给定速率限制器键的尝试次数。例如,当接收者读取给定消息时,您可以重置尝试次数:

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