在Web開發中,咱們常常會遇到須要批量處理任務的場景,好比羣發郵件、秒殺資格獲取等,咱們將這些耗時或者高併發的操做放到隊列中異步執行
能夠有效緩解系統壓力、提升系統響應速度和負載能力。php
咱們仍然從配置文件開始,首先咱們須要在配置文件中配置默認隊列驅動爲Redis。lumen沒有配置文件,能夠從laravel項目中拷貝一份config目錄過來。
隊列配置文件是config/queue.php
:html
return [ 'default' => env('QUEUE_DRIVER', 'sync'), 'connections' => [ 'database' => [ 'driver' => 'database', 'table' => 'jobs', 'queue' => 'default', 'expire' => 60, ], 'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => 'default', 'expire' => 60, ], ], 'failed' => [ 'database' => 'mysql', 'table' => 'failed_jobs', ], ];
配置文件第一個配置項default
用於指定默認的隊列驅動,修改.env中的QUEUE_DRIVER
便可。python
connections
配置項包含了Laravel支持的全部隊列驅動,咱們使用Redis驅動,因此須要配置redis項:connection對應config/database.php中redis的default配置
;queue
爲默認隊列名稱;expire
爲隊列任務過時時間(秒)。這裏咱們能夠保持其默認配置不變。mysql
failed
配置項用於配置失敗隊列任務存放的數據庫及數據表。這裏咱們須要按照本身的數據庫配置對其作相應修改。laravel
要使用 redis 隊列驅動,須要在配置文件 config/database.php
中配置 Redis
數據庫鏈接。redis
若是 Redis 隊列鏈接使用 Redis Cluster(集羣),隊列名稱必須包含 key hash tag
,以確保給定隊列對應的全部 Redis keys 都存放到同一個 hash slot
:sql
'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => '{default}', 'retry_after' => 90, ],
注:對通常中小型應用推薦使用
Redis
做爲隊列驅動。
數據庫
要使用 database
隊列驅動,你須要數據表保存任務信息(好比失敗任務)。要生成建立這些表的遷移,能夠在項目目錄下運行 Artisan
命令 queue:table
,遷移被建立以後,可使用 migrate
命令生成這些表:數據庫
php artisan queue:table php artisan queue:failed_jobs php artisan migrate
運行後生成failed_jobs
、jobs
、migrations
三張表。ubuntu
一般,全部的任務類都保存在 app/Jobs
目錄。laravel
中 app/Jobs
不存在,在運行 Artisan
命令 make:job
的時候,它將會自動建立。你能夠經過 Artisan CLI 來生成隊列任務類:緩存
php artisan make:job ProcessPodcast
生成的類都實現了 Illuminate\Contracts\Queue\ShouldQueue
接口, 告訴 Laravel 將該任務推送到隊列,而不是當即運行:
lumen
中 app/Jobs
目錄已經存在,因爲不能執行artisan命令,直接複製目錄中的ExampleJob.php
便可。該文件繼承Job.php
從而實現了ShouldQueue
。
任務類很是簡單,一般只包含處理該任務的 handle
方法,在任務被處理的時候調用,注意咱們能夠在任務的 handle 方法中進行依賴注入
。Laravel 服務容器會自動注入這些依賴。
建立好任務類後,就能夠經過任務自身的 dispatch
方法將其分發到隊列。dispatch
方法須要的惟一參數就是該任務的實例:
lumen中用法:
指定隊列任務最大失敗次數的一種實現方式是經過 Artisan 命令 --tries
切換:
php artisan queue:work --tries=3
不過,你還能夠在任務類自身定義最大失敗次數來實現更加細粒度的控制,若是最大失敗次數在任務中指定,則其優先級高於命令行
指定的數值:
<?php namespace App\Jobs; class ProcessPodcast implements ShouldQueue { /** * The number of times the job may be attempted. * * @var int */ public $tries = 5; }
注:timeout
方法爲PHP7.1+
和pcntl
擴展作了優化。
相似的,隊列任務最大運行時長(秒)能夠經過 Artisan 命令上的 --timeout
開關來指定:
php artisan queue:work --timeout=30
一樣,你也能夠在任務類中定義該任務容許運行的最大時長(單位:秒),任務中指定的超時時間優先級也高於命令行定義的數值:
<?php namespace App\Jobs; class ProcessPodcast implements ShouldQueue { /** * The number of seconds the job can run before timing out. * * @var int */ public $timeout = 120; }
除了定義在任務失敗前的最大嘗試次數外,還能夠定義在指定時間內容許任務的最大嘗試次數
,這能夠經過在任務類中添加 retryUntil
方法來實現:
/** * Determine the time at which the job should timeout. * * @return \DateTime */ public function retryUntil() { return now()->addSeconds(5); }
注:還能夠在隊列時間監聽器中定義
retryUntil
方法。
注:該功能要求應用能夠與 Redis 服務器進行交互。
若是應用使用了 Redis
,那麼可使用時間或併發
來控制隊列任務。該功能特性在隊列任務與有頻率限制的 API 交互時頗有幫助,例如,經過 throttle
方法,你能夠限定給定類型任務每 60 秒只運行 10 次。若是不能獲取鎖,須要將任務釋放回隊列以即可以再次執行:
Redis::throttle('key')->allow(10)->every(60)->then(function () { // Job logic... }, function () { // Could not obtain lock... return $this->release(10); });
注:在上面的例子中,上面的方法可能沒法找到,可是直接複製便可使用(具體還不清楚,知道的大神能夠留言指教)。key 能夠是任意能夠惟一標識你想要限定訪問頻率的任務類型的字符串。舉個例子,這個鍵能夠基於任務類名和操做 Eloquent 模型的 ID 進行構建。
除此以外,還能夠指定能夠同時處理給定任務的最大進程數量。這個功能在隊列任務正在編輯一次只能由一個任務進行處理的資源時頗有用。例如,使用 funnel 方法你能夠給定類型任務一次只能由一個工做進程進行處理:
Redis::funnel('key')->limit(1)->then(function () { // Job logic... }, function () { // Could not obtain lock... return $this->release(10); });
注:使用頻率限制時,任務在運行成功以前須要的最大嘗試次數很難權衡,所以,將頻率限制和基於時間的嘗試次數結合起來使用是個不錯的選擇。
Laravel 自帶了一個隊列進程用來處理被推送到隊列的新任務。你可使用 queue:work
命令運行這個隊列進程。請注意,隊列進程開始運行後,會持續監聽隊列,直至你手動中止或關閉終端:
php artisan queue:work
注:爲了保持隊列進程queue:work
持續在後臺運行,須要使用進程守護程序,好比 Supervisor 來確保隊列進程持續運行。簡單處理可使用
php artisan queue:work --daemon &
開始進行隊列監聽
laravel 包含了一個 Artisan 命令來運行推送到隊列中的任務的執行。你可使用 queue:listen
命令來運行監聽器:
php artisan queue:listen
注意:queue:listen
要比queue:work --daemon
性能差不少。
你也能夠指定監聽哪個鏈接的隊列:
php artisan queue:listen connection-name
請記住,
隊列進程是長生命週期的進程,會在啓動後駐留內存。若應用有任何改動將不會影響到已經啓動的進程。因此請在發佈程序後,重啓隊列進程
。
能夠經過 Aritisan 命令 queue:restart
來優雅地重啓隊列進程:
php artisan queue:restart
該命令將在隊列進程完成正在進行的任務後,結束該進程,避免隊列任務的丟失或錯誤。因爲隊列進程會在執行 queue:restart
命令後死掉,你仍然須要經過進程守護程序如 Supervisor
來自動重啓隊列進程。
注:隊列使用緩存來存儲重啓信號,因此在使用此功能前你須要驗證緩存驅動配置正確。
Supervisor
是 Linux 系統中經常使用的進程守護程序。若是隊列進程 queue:work
意外關閉,它會自動重啓啓動隊列進程。在 Ubuntu 安裝Supervisor 很是簡單:
sudo apt-get install supervisor
注:若是本身配置 Supervisor 有困難,能夠考慮使用 Laravel Forge,它會爲 Laravel 項目自動安裝並配置 Supervisor。
Supervisor 配置文件一般存放在 /etc/supervisor/conf.d
目錄,在該目錄下,能夠建立多個配置文件指示 Supervisor 如何監視進程,例如,讓咱們建立一個開啓並監視 queue:work
進程的 laravel-worker.conf
文件:
[program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command= php /home/forge/app.com/artisan queue:work redis --sleep=3 --tries=3 --daemon autostart=true autorestart=true user=forge numprocs=8 redirect_stderr=true stdout_logfile=/home/forge/app.com/worker.log
在本例中,numprocs
指令讓 Supervisor
運行 8 個 queue:work
進程並監視它們,若是失敗的話自動重啓。固然,你須要修改 queue:work sqs
的 command 指令來映射你的隊列鏈接。
啓動 Supervisor
當成功建立配置文件後,須要刷新 Supervisor
的配置信息並使用以下命令啓動進程:
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start laravel-worker:*
使用top
或者ps aux | grep php
命令能夠看到啓動的php進程。
你能夠經過 Supervisor 官方文檔獲取更多信息。
在CentOS中配置稍微有些區別:
yum -y install python-setuptools easy_install supervisor
supervisor安裝完成後會生成三個執行程序:
supervisortd
supervisor的守護進程服務(用於接收進程管理命令)
supervisorctl
客戶端(用於和守護進程通訊,發送管理進程的指令)
echo_supervisord_conf
生成初始配置文件程序。
將配置文件重定向到/etc/目錄下面
mkdir /etc/supervisor echo_supervisord_conf > /etc/supervisor/supervisord.conf
默認配置文件在/etc/supervisor/supervisord.conf
。
編輯配置文件:找到最後一行,引入自定義配置文件
;[include] ;files = conf.d/*.ini
去掉[include]
和files
前面的「;
」 include生效,在/etc/supervisor/
下建立conf.d
文件夾,在其中添加相似ubuntu
中配置文件。
mkdir conf.d
啓動:
supervisord
啓動supervisor
supervisorctl
控制supervisord
啓動後會看到一堆信息,可是不影響。
/usr/lib/python2.7/site-packages/supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security. 'Supervisord is running as root and it is searching '
可指定配置文件: supervisord -c /etc/supervisord.conf
每次修改配置後都須要重啓supervisor
才能生效
supervisorctl reload
監控狀態:
supervisorctl status
附一個sqs錯誤處理,redis方式不使用sqs
In SqsConnector.php line 26: Class 'Aws\Sqs\SqsClient' not found
使用 composer 安裝:
composer require aws/aws-sdk-php-laravel
本文參考:laravel學院