Laravel延迟队列的实现原理
Laravel延迟队列的实现原理
redis的延迟队列一般是通过有序集合zset实现的,可参考:https://blog.csdn.net/raoxiaoya/article/details/103066624
实际上laravel的延迟队列也是这个原理。
设置驱动为redis
我们使用命令行来投递两个队列,一个普通队列,一个延迟队列
public function handle()
{
$this->test1();
$this->test2();
}
// 延迟执行
public function test2()
{
$data = ['data' => Str::random(10), 'time' => date('Y-m-d H:i:s')];
dispatch(new TestJob($data))->delay(now()->addMinute(20))->onConnection('redis')->onQueue('testqueue');
}
public function test1()
{
$data = ['data' => Str::random(10), 'time' => date('Y-m-d H:i:s')];
dispatch(new TestJob($data))->onConnection('redis')->onQueue('testqueue');
}
然后查看redis,发现多了两个key
1、queues:testqueue
结构为list
目前有一个元素
{"displayName":"App\\Jobs\\TestJob","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"timeout":null,"timeoutAt":null,"data":{"commandName":"App\\Jobs\\TestJob","command":"O:16:\"App\\Jobs\\TestJob\":8:{s:4:\"data\";a:2:{s:4:\"data\";s:10:\"WKSmRQJsbs\";s:4:\"time\";s:19:\"2020-02-09 11:40:51\";}s:6:\"\u0000*\u0000job\";N;s:10:\"connection\";s:5:\"redis\";s:5:\"queue\";s:9:\"testqueue\";s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:5:\"delay\";N;s:7:\"chained\";a:0:{}}"},"id":"0NB0RK9CKQRVbbuskTWUSs8Lp91XqYzW","attempts":0}
2、queues:testqueue:delayed
结构为zset
目前有一个元素
value:{"displayName":"App\\Jobs\\TestJob","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"timeout":null,"timeoutAt":null,"data":{"commandName":"App\\Jobs\\TestJob","command":"O:16:\"App\\Jobs\\TestJob\":8:{s:4:\"data\";a:2:{s:4:\"data\";s:10:\"lIRuVmQsdK\";s:4:\"time\";s:19:\"2020-02-09 11:40:59\";}s:6:\"\u0000*\u0000job\";N;s:10:\"connection\";s:5:\"redis\";s:5:\"queue\";s:9:\"testqueue\";s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:5:\"delay\";O:25:\"Illuminate\\Support\\Carbon\":3:{s:4:\"date\";s:26:\"2020-02-09 12:00:59.123314\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:3:\"UTC\";}s:7:\"chained\";a:0:{}}"},"id":"8utPrwDVunKYZpol2DgQJdaalZy2szMc","attempts":0}
score: 1581249659
而Laravel是使用一个进程来消费所有队列的,那么它是怎样做的呢?
我们打开redis队列的具体实现:
vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php
找到pop方法,该方法负责从队列中右弹出一个元素来消费的。
public function pop($queue = null)
{
$this->migrate($prefixed = $this->getQueue($queue));
if (empty($nextJob = $this->retrieveNextJob($prefixed))) {
return;
}
[$job, $reserved] = $nextJob;
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);
}
}
public function migrateExpiredJobs($from, $to)
{
return $this->getConnection()->eval(
LuaScripts::migrateExpiredJobs(), 2, $from, $to, $this->currentTime()
);
}
public static function migrateExpiredJobs()
{
return <<<'LUA'
-- Get all of the jobs with an expired "score"...
local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])
-- If we have values in the array, we will remove them from the first queue
-- and add them onto the destination queue in chunks of 100, which moves
-- all of the appropriate jobs onto the destination queue very safely.
if(next(val) ~= nil) then
redis.call('zremrangebyrank', KEYS[1], 0, #val - 1)
for i = 1, #val, 100 do
redis.call('rpush', KEYS[2], unpack(val, i, math.min(i+99, #val)))
end
end
return val
LUA;
}
————————————————
版权声明:本文为CSDN博主「raoxiaoya」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/raoxiaoya/article/details/104241467

浙公网安备 33010602011771号