關(guān)于 Laravel Redis 多個(gè)進(jìn)程同時(shí)取隊(duì)列問(wèn)題詳解
前言
最近在工作中遇到了一個(gè)問(wèn)題,開(kāi)啟多個(gè)進(jìn)程處理隊(duì)列會(huì)重復(fù)讀取 Redis 中隊(duì)列嗎?是否因此導(dǎo)致重復(fù)執(zhí)行任務(wù)?下面就來(lái)通過(guò)示例代碼詳細(xì)介紹下。
使用 Supervisor 監(jiān)聽(tīng) Laravel 隊(duì)列任務(wù),其中 Supervisor 的配置如下:
[program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon autostart=true autorestart=true numprocs=8 redirect_stderr=true stdout_logfile=/var/www/xxx.cn/worker.log
注意: numprocs = 8
,代表開(kāi)啟 8 個(gè)進(jìn)程來(lái)執(zhí)行 command 中的命令。
如下:
PS C:\Users\tanteng\website\laradock> docker-compose exec php-worker sh /etc/supervisor/conf.d # ps -ef | grep php 7 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 8 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 9 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 10 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 11 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 12 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 13 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 14 root 0:00 php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemon 44 root 0:00 grep php
Laravel 多進(jìn)程讀取隊(duì)列內(nèi)容是否會(huì)重復(fù)
在 Laravel 的某個(gè)控制器方法,一次放入多個(gè)任務(wù)隊(duì)列:
public function index(Request $request) { $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); $this->dispatch((new SendFile3())->onQueue('sendfile')); }
在隊(duì)列處理的方法打印日志,打印處理的隊(duì)列的 ID:
app/Jobs/SendFile3.php
public function handle() { info('invoke SendFile3'); dump('invoke handle'); $rawbody = $this->job->getRawBody(); $info = json_decode($rawbody, true); info('queue id:' . $info['id']); }
Laravel 使用 Redis 的 list 作為隊(duì)列的數(shù)據(jù)結(jié)構(gòu),并會(huì)為每個(gè)隊(duì)列分配一個(gè) ID,數(shù)據(jù)結(jié)構(gòu)如下:
{ "job": "Illuminate\\Queue\\CallQueuedHandler@call", "data": { "commandName": "App\\Jobs\\SendFile3", "command": "O:18:\"App\\Jobs\\SendFile3\":4:{s:6:\"\u0000*\u0000job\";N;s:10:\"connection\";N;s:5:\"queue\";s:8:\"sendfile\";s:5:\"delay\";N;}" }, "id": "hadBcy3IpNsnOofQQdHohsa451OkQs88", "attempts": 1 }
請(qǐng)求這個(gè)控制器路由(或者命令行方式),就可以看到 Redis 中多了很多隊(duì)列任務(wù)了,如圖:
這個(gè)時(shí)候開(kāi)啟 Supervisor 處理隊(duì)列任務(wù),并查看日志:
[2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:JaClJzhDEvntzLCRIz6uRQkCVLbE8Y9C [2017-12-23 19:01:01] local.INFO: queue id:ukHv0Li4P2VgPa55qU6yEOJM27Mo5YwJ [2017-12-23 19:01:01] local.INFO: queue id:ObMpwDTmnaveBUkU7aan5abt3Agyt90l [2017-12-23 19:01:01] local.INFO: queue id:fo2qZn2ftSdQtdnKOciMK7iJb4qlhRGE [2017-12-23 19:01:01] local.INFO: queue id:uLjFMoOU7Wk7bOAd4zpHb3ccRMJHBtR6 [2017-12-23 19:01:01] local.INFO: queue id:87ULqPBObFmGr16nl5wxFVOi71zGCeRM [2017-12-23 19:01:01] local.INFO: queue id:9UVl0muQLzBqlRI99rChGW2ElXwVEMIE [2017-12-23 19:01:01] local.INFO: queue id:a0vgyZuz9HtmH7DGHEpXqesFTcQU3QAF [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:2cXuXxopPkgYiV4WO8gv9CJ6CwXeKtYL [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:9acTAYa8cxpJX6Q3Gb1sULokotP8reqZ [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:BPHQvBboChlv4gr2I0vyLVyw9bijtTYJ [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:Fm6tNajdxYKtdQbDMYDmwWJFLnNikRyg [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:nyAbcvSkBVPbaH3e2ItQkoLJlP1ficib [2017-12-23 19:01:01] local.INFO: queue id:WBHsSVZtP43569UoPXxfLLJcvYmPW7cP [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:bliPnKcRSDApwVmKLNxEhaKelhm0RDEY [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:eOAoQucEIwRz9uZ64xm6IDKgiqj9Xc3W [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:lzise9EiqQqINrhALbmAI4qNg7qylpb2 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:WXYKvcfOhS1pPnwOwUTsenoMv5l5EUXe [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:XtH5JiwLgnrwWzI02Oyi70pihAOkuJUD [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:9ehmE5HImlpNubpY0xWN8UVrOzxeMqws [2017-12-23 19:01:01] local.INFO: queue id:C1sK87cpZl47edLA0zhfo7PJ9MIEcoyx [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:2kwl51oH4lyyRrljCReGUCkNiJRDl7oe [2017-12-23 19:01:01] local.INFO: queue id:ObRpoqrYTPYiyv2delMlOXu3sAPpWJlN [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:6qgu6W3TapLjSrt688yv9HRXvDDLxntz [2017-12-23 19:01:01] local.INFO: queue id:wiTlERhwn7s9cQkfUF9lLlNADpXjKncI [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:ZSLW0VLFBDpL4wjTJzu3Yb3V45pNe807 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:qhZlXLGfGWRluIeNm7VbllmTJZYb2h5n [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:LUx1IByD3L2psNl9BZwHhk2knXyRPzW6 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:M2RESPjyo5hpAFxxL0EQbWwsUq4jpmWn [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:hUsGaiIAOO6ZfGQc5kGHGpsv5RpoRPYO [2017-12-23 19:01:01] local.INFO: queue id:cEHJsOy6bLeZ4NbncPziaHqlarMeyyEF [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:w4bkFiJKMU5saqG2xKN3ZRL5BYXGATMk [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:0zBuwbxlrEhhxKfYBkVyTY4z35f154sI [2017-12-23 19:01:01] local.INFO: queue id:mvoZvyDPvq4tcPjEy9G7PMtH3MwPkPik [2017-12-23 19:01:01] local.INFO: invoke SendFile3 [2017-12-23 19:01:01] local.INFO: queue id:TLvF74eeidECWKtjZqWvW03UJTRPTL9r [2017-12-23 19:01:01] local.INFO: queue id:me8wyPfgcz0nf9xvcXz0hf2xVxqa1FFS
這 8 個(gè)進(jìn)程并發(fā)處理隊(duì)列,但從打印的日志看,沒(méi)有出現(xiàn)同樣的 ID. 我們?cè)倏匆幌?Laravel 如何使用 Redis 處理隊(duì)列的。
分析一下 Laravel 隊(duì)列的處理
Laravel 中入隊(duì)列方法
public function pushRaw($payload, $queue = null, array $options = []) { $this->getConnection()->rpush($this->getQueue($queue), $payload); return Arr::get(json_decode($payload, true), 'id'); }
用的是 Redis 的 rpush 命令。
Laravel 中取隊(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); } }
這里用的是 lua 腳本取隊(duì)列,如下:
public static function pop() { return <<<'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} LUA; }
那么結(jié)論是:從 Laravel 的處理方式和打印的日志結(jié)果看,即使多個(gè)進(jìn)程讀取同一個(gè)隊(duì)列,也不會(huì)讀取到一樣的數(shù)據(jù)。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
thinkPHP3.x常量整理(預(yù)定義常量/路徑常量/系統(tǒng)常量)
這篇文章主要介紹了thinkPHP3.x常量,整理總結(jié)了thinkPHP3.x常用的各類常量,包括預(yù)定義常量、路徑常量及系統(tǒng)常量,需要的朋友可以參考下2016-05-05codeigniter教程之上傳視頻并使用ffmpeg轉(zhuǎn)flv示例
這篇文章主要介紹了codeigniter上傳視頻并使用ffmpeg轉(zhuǎn)成flv的示例,需要的朋友可以參考下2014-02-02PHP、Python和Javascript的裝飾器模式對(duì)比
這篇文章主要介紹了PHP、Python和Javascript的裝飾器模式對(duì)比,修飾模式(Decorator Pattern),又叫裝飾者模式,是面向?qū)ο缶幊填I(lǐng)域中,一種動(dòng)態(tài)地往一個(gè)類中添加新的行為的設(shè)計(jì)模式,需要的朋友可以參考下2015-02-02Laravel 5框架學(xué)習(xí)之路由、控制器和視圖簡(jiǎn)介
這篇文章主要介紹了Laravel 5框架學(xué)習(xí)之路由、控制器和視圖簡(jiǎn)介,十分的細(xì)致全面,需要的朋友可以參考下2015-04-04Zend Framework入門教程之Zend_Mail用法示例
這篇文章主要介紹了Zend Framework中Zend_Mail用法,結(jié)合實(shí)例形式分析了Zend Framework中Zend_Mail組件的功能、使用步驟與相關(guān)操作技巧,需要的朋友可以參考下2016-12-12PHP實(shí)現(xiàn)rar解壓讀取擴(kuò)展包小結(jié)
今天重點(diǎn)給大家介紹PHP 擴(kuò)展就是針對(duì)于 rar 的壓縮包操作,不過(guò),PHP 的 rar 擴(kuò)展僅能讀取和解壓 rar 格式的壓縮包,并不能進(jìn)行壓縮操作,關(guān)于 rar 的壓縮操作并沒(méi)有找到太多有用的資料,下面通過(guò)本文一起學(xué)習(xí)下PHP rar解壓讀取知識(shí)吧2021-06-06Laravel 5 框架入門(二)構(gòu)建 Pages 的管理功能
這篇文章主要介紹了Laravel 5 框架入門的第二篇文章,給大家講解的是構(gòu)建 Pages 的管理功能,十分的詳細(xì),有需要的小伙伴可以參考下。2015-04-04Yii2針對(duì)指定url的生成及圖片等的引入方法小結(jié)
這篇文章主要介紹了Yii2針對(duì)指定url的生成及圖片等的引入方法,針對(duì)常用的URL格式及圖片路徑操作結(jié)合實(shí)例進(jìn)行了對(duì)比分析,需要的朋友可以參考下2016-07-07