溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Laravel 中重復(fù)執(zhí)行同一個隊(duì)列任務(wù)的原因是什么

發(fā)布時間:2021-07-19 14:41:59 來源:億速云 閱讀:108 作者:Leah 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān) Laravel 中重復(fù)執(zhí)行同一個隊(duì)列任務(wù)的原因是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

源代碼文件:vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php

/**
 * The expiration time of a job.
 *
 * @var int|null
 */
protected $expire = 60;

這個 $expire 成員變量是一個固定的值,Laravel 認(rèn)為一個隊(duì)列再怎么 60 秒也該執(zhí)行完了吧。取隊(duì)列方法:

public function pop($queue = null)
{
 $original = $queue ?: $this->default; 
 $queue = $this->getQueue($queue); 
 $this->migrateExpiredJobs($queue.':delayed', $queue); 
 if (! is_null($this->expire)) {
  $this->migrateExpiredJobs($queue.':reserved', $queue);
 } 
 list($job, $reserved) = $this->getConnection()->eval(
  LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire
 ); 
 if ($reserved) {
  return new RedisJob($this->container, $this, $job, $reserved, $original);
 }
}

取隊(duì)列有幾步操作,因?yàn)殛?duì)列執(zhí)行失敗,或執(zhí)行超時等都會放入另外的集合保存起來,以便重試,過程如下:

    1.把因執(zhí)行失敗的隊(duì)列從 delayed 集合重新 rpush 到當(dāng)前執(zhí)行的隊(duì)列中。

    2.把因執(zhí)行超時的隊(duì)列從 reserved 集合重新 rpush 到當(dāng)前執(zhí)行的隊(duì)列中。

    3.然后才是從隊(duì)列中取任務(wù)開始執(zhí)行,同時把隊(duì)列放入 reserved 的有序集合。

這里使用了 eval 命令執(zhí)行這個過程,用到了幾個 lua 腳本。

從要執(zhí)行的隊(duì)列中取任務(wù):

local job = redis.call('lpop', KEYS[1])
local reserved = false
if(job ~= false) then
 reserved = cjson.decode(job)
 reserved['attempts'] = reserved['attempts'] + 1
 reserved = cjson.encode(reserved)
 redis.call('zadd', KEYS[2], ARGV[1], reserved)
end
return {job, reserved}

可以看到 Laravel 在取 Redis 要執(zhí)行的隊(duì)列的時候,同時會放一份到一個有序集合中,并使用過期時間戳作為分值。

只有當(dāng)這個任務(wù)完成后,再把有序集合中這個任務(wù)移除。從這個有序集合移除隊(duì)列的代碼就省略,我們看一下 Laravel 如何處理執(zhí)行時間大于 60 秒的隊(duì)列。

也就是這段 lua 腳本執(zhí)行的操作:

local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])
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 true

這里 zrangebyscore 找出分值從無限小到當(dāng)前時間戳的元素,也就是 60 秒之前加入到集合的任務(wù),然后通過 zremrangebyrank 從集合移除這些元素并 rpush 到隊(duì)列中。

看到這里應(yīng)該就恍然大悟了。

如果一個隊(duì)列 60 秒沒執(zhí)行完,那么進(jìn)程在取隊(duì)列的時候從 reserved 集合中把這些任務(wù)又重新 rpush 到隊(duì)列中。

關(guān)于 Laravel 中重復(fù)執(zhí)行同一個隊(duì)列任務(wù)的原因是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI