Laravel/Lumen 使用 Redis队列
一、概述
在Web開發(fā)中,我們經(jīng)常會遇到需要批量處理任務(wù)的場景,比如群發(fā)郵件、秒殺資格獲取等,我們將這些耗時或者高并發(fā)的操作放到隊列中異步執(zhí)行可以有效緩解系統(tǒng)壓力、提高系統(tǒng)響應(yīng)速度和負載能力。
二、配置文件
我們?nèi)匀粡呐渲梦募_始,首先我們需要在配置文件中配置默認隊列驅(qū)動為Redis。lumen沒有配置文件,可以從laravel項目中拷貝一份config目錄過來。
隊列配置文件是config/queue.php:
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用于指定默認的隊列驅(qū)動,修改.env中的QUEUE_DRIVER即可。
connections配置項包含了Laravel支持的所有隊列驅(qū)動,我們使用Redis驅(qū)動,所以需要配置redis項:connection對應(yīng)config/database.php中redis的default配置;queue為默認隊列名稱;expire為隊列任務(wù)過期時間(秒)。這里我們可以保持其默認配置不變。
failed配置項用于配置失敗隊列任務(wù)存放的數(shù)據(jù)庫及數(shù)據(jù)表。這里我們需要按照自己的數(shù)據(jù)庫配置對其做相應(yīng)修改。
要使用 redis 隊列驅(qū)動,需要在配置文件?config/database.php?中配置?Redis?數(shù)據(jù)庫連接。
如果 Redis 隊列連接使用 Redis Cluster(集群),隊列名稱必須包含?key hash tag,以確保給定隊列對應(yīng)的所有 Redis keys 都存放到同一個?hash slot:
'redis' => ['driver' => 'redis','connection' => 'default','queue' => '{default}','retry_after' => 90,
],
注:對一般中小型應(yīng)用推薦使用?Redis?作為隊列驅(qū)動。
三、驅(qū)動預(yù)備知識
數(shù)據(jù)庫
要使用?database?隊列驅(qū)動,你需要數(shù)據(jù)表保存任務(wù)信息(比如失敗任務(wù))。要生成創(chuàng)建這些表的遷移,可以在項目目錄下運行?Artisan?命令?queue:table,遷移被創(chuàng)建之后,可以使用?migrate?命令生成這些表:
php artisan queue:table
php artisan queue:failed-tablephp artisan migrate
運行后生成failed_jobs、jobs、migrations三張表。
四、創(chuàng)建任務(wù)
1、生成任務(wù)類
通常,所有的任務(wù)類都保存在?app/Jobs?目錄。laravel中?app/Jobs?不存在,在運行?Artisan?命令?make:job?的時候,它將會自動創(chuàng)建。你可以通過 Artisan CLI 來生成隊列任務(wù)類:
php artisan make:job ProcessPodcast
生成的類都實現(xiàn)了?Illuminate\Contracts\Queue\ShouldQueue?接口, 告訴 Laravel 將該任務(wù)推送到隊列,而不是立即運行:
lumen中?app/Jobs目錄已經(jīng)存在,由于不能執(zhí)行artisan命令,直接復(fù)制目錄中的ExampleJob.php即可。該文件繼承Job.php?從而實現(xiàn)了ShouldQueue。
2、任務(wù)類結(jié)構(gòu)
任務(wù)類非常簡單,通常只包含處理該任務(wù)的?handle?方法,在任務(wù)被處理的時候調(diào)用,注意我們可以在任務(wù)的 handle 方法中進行依賴注入。Laravel 服務(wù)容器會自動注入這些依賴。
3、分發(fā)任務(wù)
創(chuàng)建好任務(wù)類后,就可以通過任務(wù)自身的?dispatch?方法將其分發(fā)到隊列。dispatch?方法需要的唯一參數(shù)就是該任務(wù)的實例:
lumen中用法:
4、指定最大失敗次數(shù)
指定隊列任務(wù)最大失敗次數(shù)的一種實現(xiàn)方式是通過 Artisan 命令?--tries?切換:
php artisan queue:work --tries=3
不過,你還可以在任務(wù)類自身定義最大失敗次數(shù)來實現(xiàn)更加細粒度的控制,如果最大失敗次數(shù)在任務(wù)中指定,則其優(yōu)先級高于命令行指定的數(shù)值:
<?phpnamespace App\Jobs;class ProcessPodcast implements ShouldQueue{/*** The number of times the job may be attempted.** @var int*/public $tries = 5;}
5、超時
注:timeout?方法為?PHP7.1+?和?pcntl?擴展做了優(yōu)化。
類似的,隊列任務(wù)最大運行時長(秒)可以通過 Artisan 命令上的?--timeout?開關(guān)來指定:
php artisan queue:work --timeout=30
同樣,你也可以在任務(wù)類中定義該任務(wù)允許運行的最大時長(單位:秒),任務(wù)中指定的超時時間優(yōu)先級也高于命令行定義的數(shù)值:
<?phpnamespace App\Jobs;class ProcessPodcast implements ShouldQueue
{/*** The number of seconds the job can run before timing out.** @var int*/public $timeout = 120;
}
6、基于時間的嘗試次數(shù)
除了定義在任務(wù)失敗前的最大嘗試次數(shù)外,還可以定義在指定時間內(nèi)允許任務(wù)的最大嘗試次數(shù),這可以通過在任務(wù)類中添加?retryUntil?方法來實現(xiàn):
/*** Determine the time at which the job should timeout.** @return \DateTime*/
public function retryUntil()
{return now()->addSeconds(5);
}
注:還可以在隊列時間監(jiān)聽器中定義?retryUntil?方法。
7、頻率限制
注:該功能要求應(yīng)用可以與 Redis 服務(wù)器進行交互。
如果應(yīng)用使用了?Redis,那么可以使用時間或并發(fā)來控制隊列任務(wù)。該功能特性在隊列任務(wù)與有頻率限制的 API 交互時很有幫助,例如,通過?throttle?方法,你可以限定給定類型任務(wù)每 60 秒只運行 10 次。如果不能獲取鎖,需要將任務(wù)釋放回隊列以便可以再次執(zhí)行:
Redis::throttle('key')->allow(10)->every(60)->then(function () {// Job logic...
}, function () {// Could not obtain lock...return $this->release(10);
});
注:在上面的例子中,上面的方法可能無法找到,但是直接復(fù)制即可使用(具體還不清楚,知道的大神可以留言指教)。key 可以是任意可以唯一標(biāo)識你想要限定訪問頻率的任務(wù)類型的字符串。舉個例子,這個鍵可以基于任務(wù)類名和操作 Eloquent 模型的 ID 進行構(gòu)建。
8、最大進程數(shù)量
除此之外,還可以指定可以同時處理給定任務(wù)的最大進程數(shù)量。這個功能在隊列任務(wù)正在編輯一次只能由一個任務(wù)進行處理的資源時很有用。例如,使用 funnel 方法你可以給定類型任務(wù)一次只能由一個工作進程進行處理:
Redis::funnel('key')->limit(1)->then(function () {// Job logic...
}, function () {// Could not obtain lock...return $this->release(10);
});
注:使用頻率限制時,任務(wù)在運行成功之前需要的最大嘗試次數(shù)很難權(quán)衡,因此,將頻率限制和基于時間的嘗試次數(shù)結(jié)合起來使用是個不錯的選擇。
9、運行隊列進程
Laravel 自帶了一個隊列進程用來處理被推送到隊列的新任務(wù)。你可以使用?queue:work?命令運行這個隊列進程。請注意,隊列進程開始運行后,會持續(xù)監(jiān)聽隊列,直至你手動停止或關(guān)閉終端:
php artisan queue:work
注:為了保持隊列進程?queue:work?持續(xù)在后臺運行,需要使用進程守護程序,比如 Supervisor 來確保隊列進程持續(xù)運行。簡單處理可以使用?
php artisan queue:work --daemon &
10、運行隊列監(jiān)聽器
開始進行隊列監(jiān)聽
laravel 包含了一個 Artisan 命令來運行推送到隊列中的任務(wù)的執(zhí)行。你可以使用?queue:listen?命令來運行監(jiān)聽器:
php artisan queue:listen
注意:queue:listen要比queue:work --daemon?性能差很多。
你也可以指定監(jiān)聽哪一個連接的隊列:
php artisan queue:listen connection-name
請記住,隊列進程是長生命周期的進程,會在啟動后駐留內(nèi)存。若應(yīng)用有任何改動將不會影響到已經(jīng)啟動的進程。所以請在發(fā)布程序后,重啟隊列進程。
可以通過 Aritisan 命令?queue:restart?來優(yōu)雅地重啟隊列進程:
php artisan queue:restart
該命令將在隊列進程完成正在進行的任務(wù)后,結(jié)束該進程,避免隊列任務(wù)的丟失或錯誤。由于隊列進程會在執(zhí)行?queue:restart?命令后死掉,你仍然需要通過進程守護程序如?Supervisor?來自動重啟隊列進程。
注:隊列使用緩存來存儲重啟信號,所以在使用此功能前你需要驗證緩存驅(qū)動配置正確。
五、配置 Supervisor
- 安裝 Supervisor
Supervisor?是 Linux 系統(tǒng)中常用的進程守護程序。如果隊列進程?queue:work?意外關(guān)閉,它會自動重啟啟動隊列進程。在 Ubuntu 安裝Supervisor 非常簡單:
sudo apt-get install supervisor
注:如果自己配置 Supervisor 有困難,可以考慮使用 Laravel Forge,它會為 Laravel 項目自動安裝并配置 Supervisor。
- 配置 Supervisor
Supervisor 配置文件通常存放在?/etc/supervisor/conf.d?目錄,在該目錄下,可以創(chuàng)建多個配置文件指示 Supervisor 如何監(jiān)視進程,例如,讓我們創(chuàng)建一個開啟并監(jiān)視?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?進程并監(jiān)視它們,如果失敗的話自動重啟。當(dāng)然,你需要修改?queue:work sqs?的 command 指令來映射你的隊列連接。
啟動 Supervisor
當(dāng)成功創(chuàng)建配置文件后,需要刷新?Supervisor?的配置信息并使用如下命令啟動進程:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
使用top?或者ps aux | grep php?命令可以看到啟動的php進程。
你可以通過?Supervisor?官方文檔獲取更多信息。
在CentOS中配置稍微有些區(qū)別:
yum -y install python-setuptools
easy_install supervisor
supervisor安裝完成后會生成三個執(zhí)行程序:
supervisortd?supervisor的守護進程服務(wù)(用于接收進程管理命令)supervisorctl?客戶端(用于和守護進程通信,發(fā)送管理進程的指令)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/下創(chuàng)建conf.d文件夾,在其中添加類似ubuntu中配置文件。
mkdir conf.d
啟動:supervisord?啟動supervisorsupervisorctl?控制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
監(jiān)控狀態(tài):
supervisorctl status
附一個sqs錯誤處理,redis方式不使用sqs
In SqsConnector.php line 26:Class 'Aws\Sqs\SqsClient' not found
使用 composer 安裝:
composer require aws/aws-sdk-php-laravel
總結(jié)
以上是生活随笔為你收集整理的Laravel/Lumen 使用 Redis队列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Laravel7中Redis队列的使用
- 下一篇: Centos7 下安装python3及卸