280 lines
6.7 KiB
PHP
280 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace Illuminate\Queue;
|
|
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Queue\Jobs\RedisJob;
|
|
use Illuminate\Contracts\Redis\Factory as Redis;
|
|
use Illuminate\Contracts\Queue\Queue as QueueContract;
|
|
|
|
class RedisQueue extends Queue implements QueueContract
|
|
{
|
|
/**
|
|
* The Redis factory implementation.
|
|
*
|
|
* @var \Illuminate\Contracts\Redis\Factory
|
|
*/
|
|
protected $redis;
|
|
|
|
/**
|
|
* The connection name.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $connection;
|
|
|
|
/**
|
|
* The name of the default queue.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $default;
|
|
|
|
/**
|
|
* The expiration time of a job.
|
|
*
|
|
* @var int|null
|
|
*/
|
|
protected $retryAfter = 60;
|
|
|
|
/**
|
|
* Create a new Redis queue instance.
|
|
*
|
|
* @param \Illuminate\Contracts\Redis\Factory $redis
|
|
* @param string $default
|
|
* @param string $connection
|
|
* @param int $retryAfter
|
|
* @return void
|
|
*/
|
|
public function __construct(Redis $redis, $default = 'default', $connection = null, $retryAfter = 60)
|
|
{
|
|
$this->redis = $redis;
|
|
$this->default = $default;
|
|
$this->connection = $connection;
|
|
$this->retryAfter = $retryAfter;
|
|
}
|
|
|
|
/**
|
|
* Get the size of the queue.
|
|
*
|
|
* @param string $queue
|
|
* @return int
|
|
*/
|
|
public function size($queue = null)
|
|
{
|
|
$queue = $this->getQueue($queue);
|
|
|
|
return $this->getConnection()->eval(
|
|
LuaScripts::size(), 3, $queue, $queue.':delayed', $queue.':reserved'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Push a new job onto the queue.
|
|
*
|
|
* @param object|string $job
|
|
* @param mixed $data
|
|
* @param string $queue
|
|
* @return mixed
|
|
*/
|
|
public function push($job, $data = '', $queue = null)
|
|
{
|
|
return $this->pushRaw($this->createPayload($job, $data), $queue);
|
|
}
|
|
|
|
/**
|
|
* Push a raw payload onto the queue.
|
|
*
|
|
* @param string $payload
|
|
* @param string $queue
|
|
* @param array $options
|
|
* @return mixed
|
|
*/
|
|
public function pushRaw($payload, $queue = null, array $options = [])
|
|
{
|
|
$this->getConnection()->rpush($this->getQueue($queue), $payload);
|
|
|
|
return json_decode($payload, true)['id'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Push a new job onto the queue after a delay.
|
|
*
|
|
* @param \DateTimeInterface|\DateInterval|int $delay
|
|
* @param object|string $job
|
|
* @param mixed $data
|
|
* @param string $queue
|
|
* @return mixed
|
|
*/
|
|
public function later($delay, $job, $data = '', $queue = null)
|
|
{
|
|
return $this->laterRaw($delay, $this->createPayload($job, $data), $queue);
|
|
}
|
|
|
|
/**
|
|
* Push a raw job onto the queue after a delay.
|
|
*
|
|
* @param \DateTimeInterface|\DateInterval|int $delay
|
|
* @param string $payload
|
|
* @param string $queue
|
|
* @return mixed
|
|
*/
|
|
protected function laterRaw($delay, $payload, $queue = null)
|
|
{
|
|
$this->getConnection()->zadd(
|
|
$this->getQueue($queue).':delayed', $this->availableAt($delay), $payload
|
|
);
|
|
|
|
return json_decode($payload, true)['id'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Create a payload string from the given job and data.
|
|
*
|
|
* @param string $job
|
|
* @param mixed $data
|
|
* @return string
|
|
*/
|
|
protected function createPayloadArray($job, $data = '')
|
|
{
|
|
return array_merge(parent::createPayloadArray($job, $data), [
|
|
'id' => $this->getRandomId(),
|
|
'attempts' => 0,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Pop the next job off of the queue.
|
|
*
|
|
* @param string $queue
|
|
* @return \Illuminate\Contracts\Queue\Job|null
|
|
*/
|
|
public function pop($queue = null)
|
|
{
|
|
$this->migrate($prefixed = $this->getQueue($queue));
|
|
|
|
list($job, $reserved) = $this->retrieveNextJob($prefixed);
|
|
|
|
if ($reserved) {
|
|
return new RedisJob(
|
|
$this->container, $this, $job,
|
|
$reserved, $this->connectionName, $queue ?: $this->default
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Migrate any delayed or expired jobs onto the primary queue.
|
|
*
|
|
* @param string $queue
|
|
* @return void
|
|
*/
|
|
protected function migrate($queue)
|
|
{
|
|
$this->migrateExpiredJobs($queue.':delayed', $queue);
|
|
|
|
if (! is_null($this->retryAfter)) {
|
|
$this->migrateExpiredJobs($queue.':reserved', $queue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Migrate the delayed jobs that are ready to the regular queue.
|
|
*
|
|
* @param string $from
|
|
* @param string $to
|
|
* @return array
|
|
*/
|
|
public function migrateExpiredJobs($from, $to)
|
|
{
|
|
return $this->getConnection()->eval(
|
|
LuaScripts::migrateExpiredJobs(), 2, $from, $to, $this->currentTime()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the next job from the queue.
|
|
*
|
|
* @param string $queue
|
|
* @return array
|
|
*/
|
|
protected function retrieveNextJob($queue)
|
|
{
|
|
return $this->getConnection()->eval(
|
|
LuaScripts::pop(), 2, $queue, $queue.':reserved',
|
|
$this->availableAt($this->retryAfter)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Delete a reserved job from the queue.
|
|
*
|
|
* @param string $queue
|
|
* @param \Illuminate\Queue\Jobs\RedisJob $job
|
|
* @return void
|
|
*/
|
|
public function deleteReserved($queue, $job)
|
|
{
|
|
$this->getConnection()->zrem($this->getQueue($queue).':reserved', $job->getReservedJob());
|
|
}
|
|
|
|
/**
|
|
* Delete a reserved job from the reserved queue and release it.
|
|
*
|
|
* @param string $queue
|
|
* @param \Illuminate\Queue\Jobs\RedisJob $job
|
|
* @param int $delay
|
|
* @return void
|
|
*/
|
|
public function deleteAndRelease($queue, $job, $delay)
|
|
{
|
|
$queue = $this->getQueue($queue);
|
|
|
|
$this->getConnection()->eval(
|
|
LuaScripts::release(), 2, $queue.':delayed', $queue.':reserved',
|
|
$job->getReservedJob(), $this->availableAt($delay)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get a random ID string.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getRandomId()
|
|
{
|
|
return Str::random(32);
|
|
}
|
|
|
|
/**
|
|
* Get the queue or return the default.
|
|
*
|
|
* @param string|null $queue
|
|
* @return string
|
|
*/
|
|
public function getQueue($queue)
|
|
{
|
|
return 'queues:'.($queue ?: $this->default);
|
|
}
|
|
|
|
/**
|
|
* Get the connection for the queue.
|
|
*
|
|
* @return \Illuminate\Redis\Connections\Connection
|
|
*/
|
|
protected function getConnection()
|
|
{
|
|
return $this->redis->connection($this->connection);
|
|
}
|
|
|
|
/**
|
|
* Get the underlying Redis instance.
|
|
*
|
|
* @return \Illuminate\Contracts\Redis\Factory
|
|
*/
|
|
public function getRedis()
|
|
{
|
|
return $this->redis;
|
|
}
|
|
}
|