頻率限制
簡介
Laravel 中內建了一個簡單易用的頻率限制抽象功能,該功能會與專案的
若想對連入 HTTP Request 的頻率限制,請參考 Rate Limiter Middleware 的說明文件。
快取設定
一般來說,Rate Limiter 會使用專案中 cache
設定檔 default
索引鍵上所定義的預設快取。不過,我們可以在專案的 cache
設定檔中定義 limiter
索引鍵來指定 Rate Limiter 要使用哪個快取 Driver:
1'default' => 'memcached',23'limiter' => 'redis',
1'default' => 'memcached',23'limiter' => 'redis',
基礎用法
可通過 Illuminate\Support\Facades\RateLimiter
Facade 來使用 Rate Limiter。Rate Limiter 所提供的最簡單的方法是 attempt
方法,該方法會對給定閉包以給定秒數來做頻率限制。
若該回呼已無法再嘗試,則 attempt
方法會回傳 false
。若還能繼續嘗試,則 attempt
會回傳該回呼的執行結果或 true
。attempt
方法的第一個引數為 Rate Limiter 的「索引鍵」,索引鍵可以是任意字串,用來表示要被頻率限制的動作:
1use Illuminate\Support\Facades\RateLimiter;23$executed = RateLimiter::attempt(4 'send-message:'.$user->id,5 $perMinute = 5,6 function() {7 // 傳送訊息...8 }9);1011if (! $executed) {12 return 'Too many messages sent!';13}
1use Illuminate\Support\Facades\RateLimiter;23$executed = RateLimiter::attempt(4 'send-message:'.$user->id,5 $perMinute = 5,6 function() {7 // 傳送訊息...8 }9);1011if (! $executed) {12 return 'Too many messages sent!';13}
如有需要,可以為 attempt
方法提供第四個「Decay Rate」引數,或是直到頻率限制被重設前的秒數。例如,我們可以將上面的範例改為每 2 分鐘允許 5 次嘗試:
1$executed = RateLimiter::attempt(2 'send-message:'.$user->id,3 $perTwoMinutes = 5,4 function() {5 // 傳送訊息...6 },7 $decayRate = 120,8);
1$executed = RateLimiter::attempt(2 'send-message:'.$user->id,3 $perTwoMinutes = 5,4 function() {5 // 傳送訊息...6 },7 $decayRate = 120,8);
手動增加嘗試次數
若想手動使用 Rate Limiter,則還有其他許多能使用的方法。舉例來說,我們可以叫用 tooManyAttempts
方法來判斷給定的 Rate Limiter 索引鍵是否已遇到其每分鐘所允許的最大嘗試次數:
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {4 return 'Too many attempts!';5}67RateLimiter::hit('send-message:'.$user->id);89// 傳送訊息...
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {4 return 'Too many attempts!';5}67RateLimiter::hit('send-message:'.$user->id);89// 傳送訊息...
或者,也可以使用 remaining
方法來取得給定索引鍵剩下的嘗試次數。若給定的索引鍵還有可嘗試的次數,則可叫用 hit
方法來增加總嘗試次數:
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {4 RateLimiter::hit('send-message:'.$user->id);56 // 傳送訊息...7}
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {4 RateLimiter::hit('send-message:'.$user->id);56 // 傳送訊息...7}
判斷 Limiter 是否可用
若某個索引鍵已無可用的嘗試次數,則 availableIn
方法會回傳距離下次可獲得嘗試次數的剩餘秒數:
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {4 $seconds = RateLimiter::availableIn('send-message:'.$user->id);56 return 'You may try again in '.$seconds.' seconds.';7}89RateLimiter::hit('send-message:'.$user->id);1011// 傳送訊息...
1use Illuminate\Support\Facades\RateLimiter;23if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {4 $seconds = RateLimiter::availableIn('send-message:'.$user->id);56 return 'You may try again in '.$seconds.' seconds.';7}89RateLimiter::hit('send-message:'.$user->id);1011// 傳送訊息...
清除嘗試次數
可使用 clear
方法來重設給定 Rate Limiter 索引鍵的嘗試次數。舉例來說,我們可以在收件人已閱讀某個訊息後重設嘗試次數:
1use App\Models\Message;2use Illuminate\Support\Facades\RateLimiter;34/**5 * Mark the message as read.6 */7public function read(Message $message): Message8{9 $message->markAsRead();1011 RateLimiter::clear('send-message:'.$message->user_id);1213 return $message;14}
1use App\Models\Message;2use Illuminate\Support\Facades\RateLimiter;34/**5 * Mark the message as read.6 */7public function read(Message $message): Message8{9 $message->markAsRead();1011 RateLimiter::clear('send-message:'.$message->user_id);1213 return $message;14}