前言
最近在工作中遇到了一個問題,開啟多個進程處理隊列會重復讀取 Redis 中隊列嗎?是否因此導致重復執(zhí)行任務(wù)?下面就來通過示例代碼詳細介紹下。
使用 Supervisor 監(jiān)聽 Laravel 隊列任務(wù),其中 Supervisor 的配置如下:
[program:laravel-worker]process_name=%(program_name)s_%(process_num)02dcommand=php /var/www/xxx.cn/artisan queue:work --queue=sendfile --tries=3 --daemonautostart=trueautorestart=truenumprocs=8redirect_stderr=truestdout_logfile=/var/www/xxx.cn/worker.log
注意: numprocs = 8,代表開啟 8 個進程來執(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 多進程讀取隊列內(nèi)容是否會重復
在 Laravel 的某個控制器方法,一次放入多個任務(wù)隊列:
html' target='_blank'>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 }
在隊列處理的方法打印日志,打印處理的隊列的 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 作為隊列的數(shù)據(jù)結(jié)構(gòu),并會為每個隊列分配一個 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}
請求這個控制器路由(或者命令行方式),就可以看到 Redis 中多了很多隊列任務(wù)了,如圖:
這個時候開啟 Supervisor 處理隊列任務(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 個進程并發(fā)處理隊列,但從打印的日志看,沒有出現(xiàn)同樣的 ID. 我們再看一下 Laravel 如何使用 Redis 處理隊列的。
分析一下 Laravel 隊列的處理
Laravel 中入隊列方法
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 中取隊列方法
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 腳本取隊列,如下:
public static function pop() return LUA local job = redis.call( lpop , KEYS[1])local reserved = falseif(job ~= false) thenreserved = cjson.decode(job)reserved[ attempts ] = reserved[ attempts ] + 1reserved = cjson.encode(reserved)redis.call( zadd , KEYS[2], ARGV[1], reserved)return {job, reserved}}
那么結(jié)論是:從 Laravel 的處理方式和打印的日志結(jié)果看,即使多個進程讀取同一個隊列,也不會讀取到一樣的數(shù)據(jù)。
總結(jié)
您可能感興趣的文章:php-msf源碼的詳解
thinkphp5 URL和路由的功能詳解與實例講解
PHP基于雙向鏈表與排序操作實現(xiàn)的會員排名功能示例_php技巧
以上就是關(guān)于 Laravel Redis 多個進程同時取隊列的問題詳解的詳細內(nèi)容,PHP教程
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。
新聞熱點
疑難解答
圖片精選