中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Laravel會不會重復執行同一個隊列任務

發布時間:2021-04-29 13:41:29 來源:億速云 閱讀:202 作者:小新 欄目:編程語言

這篇文章主要介紹Laravel會不會重復執行同一個隊列任務,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

Laravel 是什么

Laravel 是一套簡潔、優雅的PHP Web開發框架。它可以讓你從面條一樣雜亂的代碼中解脫出來;它可以幫你構建一個完美的網絡APP,而且每行代碼都可以簡潔、富于表達力。

在 Laravel 中使用 Redis 處理隊列任務,框架提供的功能非常強大,但是最近遇到一個問題,就是發現一個任務被多次執行,這是為什么呢?

先說原因:因為在 Laravel 中如果一個隊列(任務)執行時間大于 60 秒,就會被認為執行失敗并重新加入隊列中,這樣就會導致重復執行同一個任務。

這個任務的邏輯就是給用戶推送內容,需要根據隊列內容取出用戶并遍歷,通過請求后端 HTTP 接口發送。比如有 10000 個用戶,在用戶數量多或接口處理速度沒那么快的情況下,執行時間肯定會大于 60 秒,于是這個任務就被重新加入隊列。情況更糟糕一點,前面的任務如果都沒有在 60 秒執行完,就都會重新加入隊列,這樣同一個任務就不止重復執行一次了,而是多次。

下面從 Laravel 源代碼找一下罪魁禍首。

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

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

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

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);
    }
}

取隊列有幾步操作,因為隊列執行失敗,或執行超時等都會放入另外的集合保存起來,以便重試,過程如下:

1.把因執行失敗的隊列從 delayed 集合重新 rpush 到當前執行的隊列中。

2.把因執行超時的隊列從 reserved 集合重新 rpush 到當前執行的隊列中。

3.然后才是從隊列中取任務開始執行,同時把隊列放入 reserved 的有序集合。

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

從要執行的隊列中取任務:

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 要執行的隊列的時候,同時會放一份到一個有序集合中,并使用過期時間戳作為分值。

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

也就是這段 lua 腳本執行的操作:

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 找出分值從無限小到當前時間戳的元素,也就是 60 秒之前加入到集合的任務,然后通過 zremrangebyrank 從集合移除這些元素并 rpush 到隊列中。

看到這里應該就恍然大悟了。

如果一個隊列 60 秒沒執行完,那么進程在取隊列的時候從 reserved 集合中把這些任務又重新 rpush 到隊列中。

以上是“Laravel會不會重復執行同一個隊列任務”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永嘉县| 革吉县| 宁武县| 苗栗县| 富平县| 屏东县| 交口县| 德清县| 昌图县| 满城县| 万宁市| 石泉县| 天等县| 金堂县| 郯城县| 永新县| 南丹县| 新竹县| 诸暨市| 达日县| 彭水| 合山市| 安塞县| 遂宁市| 柘荣县| 错那县| 长宁县| 武陟县| 马龙县| 镶黄旗| 电白县| 南华县| 临洮县| 和顺县| 石台县| 五大连池市| 巨野县| 瑞安市| 盐池县| 招远市| 南靖县|