Laravel 速率限制器
Laravel RateLimiter
我是 Laravel 的新手,目前使用的 API 限制为每分钟 25 个请求。
我有一个控制器方法 sendRequest()
,所有方法都使用它向 API 发送请求,所以我认为这是放置速率限制器的地方,它检查当前请求是否可以添加到如果尚未达到限制,请排队。
我在想这样的事情:
protected function sendRequest(){
if ($this->allowRequest()) {
//proceed to api call
}
}
protected function allowRequest() {
$allow = false;
//probably a do-while loop to check if the limit has been reached within the timeframe?
}
我发现这个 class Illuminate\Cache\RateLimiter
我认为可能有用,但还不知道如何使用它。谁能用这个直接指出我正确的方向?所以基本上请求应该“等待”并仅在未达到 25 requests/minute 限制时执行。
谢谢!
Illuminate\Cache\RateLimiter
class 有 hit
和 tooManyAttempts
方法,您可以像这样使用:
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Request;
protected function sendRequest()
{
if ($this->hasTooManyRequests()) {
// wait
sleep(
$this->limiter()
->availableIn($this->throttleKey()) + 1 // <= optional plus 1 sec to be on safe side
);
// Call this function again.
return $this->sendRequest();
}
//proceed to api call
$response = apiCall();
// Increment the attempts
$this->limiter()->hit(
$this->throttleKey(), 60 // <= 60 seconds
);
return $response;
}
/**
* Determine if we made too many requests.
*
* @return bool
*/
protected function hasTooManyRequests()
{
return $this->limiter()->tooManyAttempts(
$this->throttleKey(), 25 // <= max attempts per minute
);
}
/**
* Get the rate limiter instance.
*
* @return \Illuminate\Cache\RateLimiter
*/
protected function limiter()
{
return app(RateLimiter::class);
}
/**
* Get the throttle key for the given request.
*
* @return string
*/
protected function throttleKey()
{
return 'custom_api_request';
}
有关更多可用方法,请参阅 Illuminate\Cache\RateLimiter
class。
您也可以查看 Illuminate\Foundation\Auth\ThrottlesLogins
作为示例,了解如何使用 Illuminate\Cache\RateLimiter
class。
注意:RateLimiter
方法使用秒而不是分钟,因为 Laravel >= 5.8 并在 [=31 上获得了 major improvement =].
在这里您将需要共享计时器,例如控制传出请求的频率上限。创建一个 class,它将成为 Laravel 应用程序的单例,并且可以在请求中共享。
class FrequencyCapper{
protected $start, $call, $request_frequency, $limit_interval;
public function __construct($frequency, $interval_in_minute){
$this->start = time();
$this->call = 0;
$this->request_frequency = frequency; // frequency of call
$this->limit_interval = $interval_in_minute; // in minutes
}
protected function allowRequest(){
$diff = time() - $this->start;
if($diff >= 60 * $this->limit_interval){
$this->start = time();
$this->call = 0;
}
return $diff < 60 * $this->limit_interval && $this->call < $this->request_frequency){
$this->call++;
return true;
}else{
return false;
}
}
}
现在,将此 class 作为单例附加到 laravel 服务容器中。在 App\Providers\AppServiceProvider.php
的引导方法中绑定单例。
$this->app->singleton('FrequencyCapper', function ($app) {
return new FrequencyCapper(25, 1); //max 25 request per minute
});
现在,这个 class 将作为依赖项对所有控制器可用。您可以将 FrequencyCapper
依赖项注入给定的任何控制器方法,
class MyController extends Controller{
protected function sendRequest(FrequencyCapper $capper){
if($capper->allowRequest()){
//you can call the api
}
}
}
如果你愿意,你可以在FrequencyCapper
class中使用microtime()
代替time()。如果您想将传入请求限制为您自己的 api,您可以使用 laravel 的 throttle
中间件,
Route::group(['prefix' => 'api', 'middleware' => 'throttle:25,1'], function(){
//define your routes here
});
对于Laravel 8和PHP 7.4我以
为例
并使这个特征:
<?php
namespace App\Traits;
use Closure;
use Illuminate\Cache\RateLimiter;
trait CustomRateLimiter {
protected string $throttleKey = 'GeneralRateLimit';
/**
* Determine if we made too many requests.
*
* @param int $maxAttempts
*
* @return bool
*/
protected function hasTooManyRequests( $maxAttempts = 10 ): bool {
return $this->limiter()->tooManyAttempts(
$this->throttleKey(), $maxAttempts // <= max attempts per minute
);
}
/**
* Get the rate limiter instance.
*
* @return RateLimiter
*/
protected function limiter(): RateLimiter {
return app( RateLimiter::class );
}
/**
* Get the throttle key for the given request.
*
* @param string $key
*
* @return string
*/
protected function throttleKey( $key = 'GeneralRateLimit' ): string {
return $this->throttleKey ?? $key;
}
/**
* @param Closure $callback Anonymous function to be executed - example: function(){ return realFunction();}
* @param int $maxAttempts Maximum number of hits before process sleeps
* @param string $throttleKey If you have different Apis, change this key to a single key.
* @param int $decaySeconds Time that will sleep when the condition of $maxAttempts is fulfilled
* @param int $optionalSecond Optional plus secs to be on safe side
*
* @return mixed
*/
protected function sendRequest( Closure $callback, $maxAttempts = 10, $throttleKey = 'GeneralRateLimit', $decaySeconds = 1, $optionalSecond = 1 ) {
$this->throttleKey = $throttleKey;
if ( $this->hasTooManyRequests( $maxAttempts ) ) {
// wait
sleep( $this->limiter()->availableIn( $this->throttleKey() ) + $optionalSecond );
// Call this function again.
return $this->sendRequest( $callback, $maxAttempts, $throttleKey, $decaySeconds, $optionalSecond );
}
//proceed to api call
$response = $callback();
// Increment the attempts
$this->limiter()->hit(
$this->throttleKey(), $decaySeconds // <= 1 seconds
);
return $response;
}
}
如何使用?
use App\Traits\CustomRateLimiter;
class MyClass {
use CustomRateLimiter;
public function realApiToCall($var1){
// custom logic
}
public function apiCall($var1){
$apiResponse = $this->sendRequest( function () use ( $var1 ) {
return $this->realApiToCall($var1);
}, 4, 'customKey1', 1 );
}
}
我是 Laravel 的新手,目前使用的 API 限制为每分钟 25 个请求。
我有一个控制器方法 sendRequest()
,所有方法都使用它向 API 发送请求,所以我认为这是放置速率限制器的地方,它检查当前请求是否可以添加到如果尚未达到限制,请排队。
我在想这样的事情:
protected function sendRequest(){
if ($this->allowRequest()) {
//proceed to api call
}
}
protected function allowRequest() {
$allow = false;
//probably a do-while loop to check if the limit has been reached within the timeframe?
}
我发现这个 class Illuminate\Cache\RateLimiter
我认为可能有用,但还不知道如何使用它。谁能用这个直接指出我正确的方向?所以基本上请求应该“等待”并仅在未达到 25 requests/minute 限制时执行。
谢谢!
Illuminate\Cache\RateLimiter
class 有 hit
和 tooManyAttempts
方法,您可以像这样使用:
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Request;
protected function sendRequest()
{
if ($this->hasTooManyRequests()) {
// wait
sleep(
$this->limiter()
->availableIn($this->throttleKey()) + 1 // <= optional plus 1 sec to be on safe side
);
// Call this function again.
return $this->sendRequest();
}
//proceed to api call
$response = apiCall();
// Increment the attempts
$this->limiter()->hit(
$this->throttleKey(), 60 // <= 60 seconds
);
return $response;
}
/**
* Determine if we made too many requests.
*
* @return bool
*/
protected function hasTooManyRequests()
{
return $this->limiter()->tooManyAttempts(
$this->throttleKey(), 25 // <= max attempts per minute
);
}
/**
* Get the rate limiter instance.
*
* @return \Illuminate\Cache\RateLimiter
*/
protected function limiter()
{
return app(RateLimiter::class);
}
/**
* Get the throttle key for the given request.
*
* @return string
*/
protected function throttleKey()
{
return 'custom_api_request';
}
有关更多可用方法,请参阅 Illuminate\Cache\RateLimiter
class。
您也可以查看 Illuminate\Foundation\Auth\ThrottlesLogins
作为示例,了解如何使用 Illuminate\Cache\RateLimiter
class。
注意:RateLimiter
方法使用秒而不是分钟,因为 Laravel >= 5.8 并在 [=31 上获得了 major improvement =].
在这里您将需要共享计时器,例如控制传出请求的频率上限。创建一个 class,它将成为 Laravel 应用程序的单例,并且可以在请求中共享。
class FrequencyCapper{
protected $start, $call, $request_frequency, $limit_interval;
public function __construct($frequency, $interval_in_minute){
$this->start = time();
$this->call = 0;
$this->request_frequency = frequency; // frequency of call
$this->limit_interval = $interval_in_minute; // in minutes
}
protected function allowRequest(){
$diff = time() - $this->start;
if($diff >= 60 * $this->limit_interval){
$this->start = time();
$this->call = 0;
}
return $diff < 60 * $this->limit_interval && $this->call < $this->request_frequency){
$this->call++;
return true;
}else{
return false;
}
}
}
现在,将此 class 作为单例附加到 laravel 服务容器中。在 App\Providers\AppServiceProvider.php
的引导方法中绑定单例。
$this->app->singleton('FrequencyCapper', function ($app) {
return new FrequencyCapper(25, 1); //max 25 request per minute
});
现在,这个 class 将作为依赖项对所有控制器可用。您可以将 FrequencyCapper
依赖项注入给定的任何控制器方法,
class MyController extends Controller{
protected function sendRequest(FrequencyCapper $capper){
if($capper->allowRequest()){
//you can call the api
}
}
}
如果你愿意,你可以在FrequencyCapper
class中使用microtime()
代替time()。如果您想将传入请求限制为您自己的 api,您可以使用 laravel 的 throttle
中间件,
Route::group(['prefix' => 'api', 'middleware' => 'throttle:25,1'], function(){
//define your routes here
});
对于Laravel 8和PHP 7.4我以
并使这个特征:
<?php
namespace App\Traits;
use Closure;
use Illuminate\Cache\RateLimiter;
trait CustomRateLimiter {
protected string $throttleKey = 'GeneralRateLimit';
/**
* Determine if we made too many requests.
*
* @param int $maxAttempts
*
* @return bool
*/
protected function hasTooManyRequests( $maxAttempts = 10 ): bool {
return $this->limiter()->tooManyAttempts(
$this->throttleKey(), $maxAttempts // <= max attempts per minute
);
}
/**
* Get the rate limiter instance.
*
* @return RateLimiter
*/
protected function limiter(): RateLimiter {
return app( RateLimiter::class );
}
/**
* Get the throttle key for the given request.
*
* @param string $key
*
* @return string
*/
protected function throttleKey( $key = 'GeneralRateLimit' ): string {
return $this->throttleKey ?? $key;
}
/**
* @param Closure $callback Anonymous function to be executed - example: function(){ return realFunction();}
* @param int $maxAttempts Maximum number of hits before process sleeps
* @param string $throttleKey If you have different Apis, change this key to a single key.
* @param int $decaySeconds Time that will sleep when the condition of $maxAttempts is fulfilled
* @param int $optionalSecond Optional plus secs to be on safe side
*
* @return mixed
*/
protected function sendRequest( Closure $callback, $maxAttempts = 10, $throttleKey = 'GeneralRateLimit', $decaySeconds = 1, $optionalSecond = 1 ) {
$this->throttleKey = $throttleKey;
if ( $this->hasTooManyRequests( $maxAttempts ) ) {
// wait
sleep( $this->limiter()->availableIn( $this->throttleKey() ) + $optionalSecond );
// Call this function again.
return $this->sendRequest( $callback, $maxAttempts, $throttleKey, $decaySeconds, $optionalSecond );
}
//proceed to api call
$response = $callback();
// Increment the attempts
$this->limiter()->hit(
$this->throttleKey(), $decaySeconds // <= 1 seconds
);
return $response;
}
}
如何使用?
use App\Traits\CustomRateLimiter;
class MyClass {
use CustomRateLimiter;
public function realApiToCall($var1){
// custom logic
}
public function apiCall($var1){
$apiResponse = $this->sendRequest( function () use ( $var1 ) {
return $this->realApiToCall($var1);
}, 4, 'customKey1', 1 );
}
}