限流
简介
Laravel 内置了简单易用的限流抽象。结合应用的缓存,可以轻松在指定时间窗口内限制任意操作。
INFO
若要限制传入的 HTTP 请求,请参阅限流中间件文档。
缓存配置
通常,限流器会使用应用 cache 配置文件中 default 键所定义的默认缓存。不过,你也可以在 cache 配置文件中定义 limiter 键,指定限流器应使用的缓存驱动:
'default' => env('CACHE_STORE', 'database'),
'limiter' => 'redis', // [tl! add]基本用法
可通过 Illuminate\Support\Facades\RateLimiter Facade 与限流器交互。最简单的方法是 attempt:它会在给定秒数内对指定回调进行限流。
当回调已无剩余尝试次数时,attempt 返回 false;否则返回回调的结果或 true。attempt 的第一个参数是限流「键」,可以是你选择的任意字符串,用于表示被限流的操作:
use Illuminate\Support\Facades\RateLimiter;
$executed = RateLimiter::attempt(
'send-message:'.$user->id,
$perMinute = 5,
function() {
// Send message...
}
);
if (! $executed) {
return 'Too many messages sent!';
}如有需要,可为 attempt 提供第四个参数,即「衰减速率」——可用尝试次数重置前的秒数。例如,可将上面的示例改为每两分钟允许五次尝试:
$executed = RateLimiter::attempt(
'send-message:'.$user->id,
$perTwoMinutes = 5,
function() {
// Send message...
},
$decayRate = 120,
);手动增加尝试次数
若希望手动与限流器交互,还有多种其他方法可用。例如,可调用 tooManyAttempts 判断给定限流键是否已超过每分钟允许的最大尝试次数:
use Illuminate\Support\Facades\RateLimiter;
if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
return 'Too many attempts!';
}
RateLimiter::increment('send-message:'.$user->id);
// Send message...当对可能收到大量并发请求的端点限流时,你可能希望检查 increment 的返回值,而不是将 tooManyAttempts 与 increment 分开调用。在使用 redis、memcached 或 database 缓存存储时,该值会原子递增,确保每个并发请求获得唯一计数:
use Illuminate\Support\Facades\RateLimiter;
$perMinute = 5;
if (RateLimiter::increment('send-message:'.$user->id) > $perMinute) {
return 'Too many attempts!';
}
// Send message...或者,可用 remaining 方法获取给定键的剩余尝试次数。若仍有剩余次数,可调用 increment 增加总尝试次数:
use Illuminate\Support\Facades\RateLimiter;
if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {
RateLimiter::increment('send-message:'.$user->id);
// Send message...
}若希望将给定限流键的值一次增加超过 1,可向 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 'You may try again in '.$seconds.' seconds.';
}
RateLimiter::increment('send-message:'.$user->id);
// Send message...清除尝试次数
可使用 clear 方法重置给定限流键的尝试次数。例如,当接收方已读某条消息时,可重置尝试次数:
use App\Models\Message;
use Illuminate\Support\Facades\RateLimiter;
/**
* Mark the message as read.
*/
public function read(Message $message): Message
{
$message->markAsRead();
RateLimiter::clear('send-message:'.$message->user_id);
return $message;
}