關於 Laravel Redis 多個進程同時取隊列的問題詳解

來源:互聯網
上載者:User
這篇文章主要給大家介紹了關於 Laravel Redis 多個進程同時取隊列問題的相關資料,文中通過範例程式碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友下面來一起學習學習吧。

前言

最近在工作中遇到了一個問題,開啟多個進程處理隊列會重複讀取 Redis 中隊列嗎?是否因此導致重複執行任務?下面就來通過範例程式碼詳細介紹下。

使用 Supervisor 監聽 Laravel 隊列任務,其中 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 個進程來執行 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 多進程讀取隊列內容是否會重複

在 Laravel 的某個控制器方法,一次放入多個任務隊列:

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 作為隊列的資料結構,並會為每個隊列分配一個 ID,資料結構如下:

{ "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 中多了很多隊列任務了,

這個時候開啟 Supervisor 處理隊列任務,並查看日誌:

[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 個進程並發處理隊列,但從列印的日誌看,沒有出現同樣的 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)endreturn {job, reserved}LUA;}

那麼結論是:從 Laravel 的處理方式和列印的日誌結果看,即使多個進程讀取同一個隊列,也不會讀取到一樣的資料。

總結

您可能感興趣的文章:

php-msf源碼的詳解

thinkphp5 URL和路由的功能詳解與執行個體講解

PHP基於雙向鏈表與排序操作實現的會員排名功能樣本_php技巧

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.