RateLimitedWithRedis.php
TLDR
The file RateLimitedWithRedis.php
defines a class RateLimitedWithRedis
that extends another class RateLimited
and implements middleware functionality for rate limiting jobs. It uses Redis as a backend to store and manage rate limiting data.
Methods
handleJob
This method is responsible for handling a rate limited job. It takes in the job object, a callback function for the next middleware, and an array of rate limit configurations. It iterates over the rate limit configurations and checks if the current job has exceeded the rate limit. If the limit is exceeded, it either releases the job for retry if shouldRelease
is true, or returns false to discard the job. If the limit is not exceeded, it proceeds to the next middleware.
tooManyAttempts
This method determines if the given key (job identifier) has been "accessed" too many times within the specified max attempts and decay seconds. It creates a new DurationLimiter
object using the Redis factory and the given key, max attempts, and decay seconds. It attempts to acquire the rate limit, and returns the negation of the result. It also updates the decaysAt
array with the decay timestamp for the given key.
getTimeUntilNextRetry
This method calculates the number of seconds that should elapse before the job is retried. It subtracts the current time from the decaysAt
timestamp for the given key, and adds 3 seconds as a buffer time.
__construct
This is the constructor method for the RateLimitedWithRedis
class. It takes a limiterName
parameter and calls the parent constructor from RateLimited
. It creates an instance of the Redis factory using the container and assigns it to the $redis
property.
__wakeup
This method is called after the object is unserialized. It is responsible for recreating the Redis factory instance from the container and assigning it to the $redis
property.
Classes
RateLimitedWithRedis
This class extends the RateLimited
class and implements middleware functionality for rate limiting jobs using Redis as the backend. It has a $redis
property to hold the Redis factory instance and a $decaysAt
property to store the decay timestamp for each job. It provides methods to handle rate limited jobs, check if a job has exceeded the rate limit, calculate the time until next retry, and handle object serialization/deserialization.
<?php
namespace Illuminate\Queue\Middleware;
use Illuminate\Container\Container;
use Illuminate\Contracts\Redis\Factory as Redis;
use Illuminate\Redis\Limiters\DurationLimiter;
use Illuminate\Support\InteractsWithTime;
class RateLimitedWithRedis extends RateLimited
{
use InteractsWithTime;
/**
* The Redis factory implementation.
*
* @var \Illuminate\Contracts\Redis\Factory
*/
protected $redis;
/**
* The timestamp of the end of the current duration by key.
*
* @var array
*/
public $decaysAt = [];
/**
* Create a new middleware instance.
*
* @param string $limiterName
* @return void
*/
public function __construct($limiterName)
{
parent::__construct($limiterName);
$this->redis = Container::getInstance()->make(Redis::class);
}
/**
* Handle a rate limited job.
*
* @param mixed $job
* @param callable $next
* @param array $limits
* @return mixed
*/
protected function handleJob($job, $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decaySeconds)) {
return $this->shouldRelease
? $job->release($this->getTimeUntilNextRetry($limit->key))
: false;
}
}
return $next($job);
}
/**
* Determine if the given key has been "accessed" too many times.
*
* @param string $key
* @param int $maxAttempts
* @param int $decaySeconds
* @return bool
*/
protected function tooManyAttempts($key, $maxAttempts, $decaySeconds)
{
$limiter = new DurationLimiter(
$this->redis, $key, $maxAttempts, $decaySeconds
);
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
$this->decaysAt[$key] = $limiter->decaysAt;
});
}
/**
* Get the number of seconds that should elapse before the job is retried.
*
* @param string $key
* @return int
*/
protected function getTimeUntilNextRetry($key)
{
return ($this->decaysAt[$key] - $this->currentTime()) + 3;
}
/**
* Prepare the object after unserialization.
*
* @return void
*/
public function __wakeup()
{
parent::__wakeup();
$this->redis = Container::getInstance()->make(Redis::class);
}
}