laravel/lumen 使用 redis隊列

1、概述

在Web開發中,咱們常常會遇到須要批量處理任務的場景,好比羣發郵件、秒殺資格獲取等,咱們將這些耗時或者高併發的操做放到隊列中異步執行能夠有效緩解系統壓力、提升系統響應速度和負載能力。php

2、配置文件

咱們仍然從配置文件開始,首先咱們須要在配置文件中配置默認隊列驅動爲Redis。lumen沒有配置文件,能夠從laravel項目中拷貝一份config目錄過來。
隊列配置文件是config/queue.phphtml

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 slotsql

'redis' => [
    'driver' => 'redis',
    'connection' => 'default',
    'queue' => '{default}',
    'retry_after' => 90,
],
注:對通常中小型應用推薦使用 Redis 做爲隊列驅動。

3、驅動預備知識

數據庫
要使用 database 隊列驅動,你須要數據表保存任務信息(好比失敗任務)。要生成建立這些表的遷移,能夠在項目目錄下運行 Artisan 命令 queue:table,遷移被建立以後,可使用 migrate 命令生成這些表:數據庫

php artisan queue:table
php artisan queue:failed_jobs

php artisan migrate

運行後生成failed_jobsjobsmigrations三張表。ubuntu

4、建立任務

一、生成任務類

一般,全部的任務類都保存在 app/Jobs 目錄。laravelapp/Jobs 不存在,在運行 Artisan 命令 make:job 的時候,它將會自動建立。你能夠經過 Artisan CLI 來生成隊列任務類:緩存

php artisan make:job ProcessPodcast

生成的類都實現了 Illuminate\Contracts\Queue\ShouldQueue 接口, 告訴 Laravel 將該任務推送到隊列,而不是當即運行:

clipboard.png

lumenapp/Jobs目錄已經存在,因爲不能執行artisan命令,直接複製目錄中的ExampleJob.php便可。該文件繼承Job.php 從而實現了ShouldQueue

clipboard.png

clipboard.png

clipboard.png

二、任務類結構

任務類很是簡單,一般只包含處理該任務的 handle 方法,在任務被處理的時候調用,注意咱們能夠在任務的 handle 方法中進行依賴注入。Laravel 服務容器會自動注入這些依賴。

三、分發任務

建立好任務類後,就能夠經過任務自身的 dispatch 方法將其分發到隊列。dispatch 方法須要的惟一參數就是該任務的實例:

clipboard.png

lumen中用法:

clipboard.png

四、指定最大失敗次數

指定隊列任務最大失敗次數的一種實現方式是經過 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 來自動重啓隊列進程。

注:隊列使用緩存來存儲重啓信號,因此在使用此功能前你須要驗證緩存驅動配置正確。

5、配置 Supervisor

  • 安裝 Supervisor

Supervisor 是 Linux 系統中經常使用的進程守護程序。若是隊列進程 queue:work 意外關閉,它會自動重啓啓動隊列進程。在 Ubuntu 安裝Supervisor 很是簡單:

sudo apt-get install supervisor
注:若是本身配置 Supervisor 有困難,能夠考慮使用 Laravel Forge,它會爲 Laravel 項目自動安裝並配置 Supervisor。
  • 配置 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

clipboard.png

使用 composer 安裝:

composer require aws/aws-sdk-php-laravel

本文參考:laravel學院

相關文章
相關標籤/搜索