频率限制
简介
Laravel 包含一个易于使用的频率限制抽象,它与应用程序的 缓存 相结合,提供了一种在指定时间窗口内限制任何操作的简单方法。
NOTE
如果你对限制传入 HTTP 请求的频率感兴趣,请查阅 频率限制器中间件文档。
缓存配置
通常,频率限制器使用应用程序 cache 配置文件中 default 键定义的默认应用程序缓存。但是,你可以通过在应用程序的 cache 配置文件中定义一个 limiter 键来指定频率限制器应使用的缓存驱动:
'default' => env('CACHE_STORE', 'database'),
'limiter' => 'redis', 基本用法
可以使用 Illuminate\Support\Facades\RateLimiter 门面 与频率限制器进行交互。频率限制器提供的最简单的方法是 attempt 方法,它在给定的秒数内对给定的回调进行频率限制。
当回调没有剩余尝试次数时,attempt 方法返回 false;否则,attempt 方法将返回回调的结果或 true。attempt 方法接受的第一个参数是频率限制器“键”,它可以是代表正在被限制频率的操作的任何字符串:
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;
}