Laravel Job Queue with horizon

林芷蕾Alicia
5 min readAug 16, 2023

--

選用Queue Driver

最簡單的方式是使用database作為job queue,像是mysql,但如果當job的數量一多,就會佔用到原有資料庫的資源,於是改選用redis,這個的壞處就是job durable的問題,我們後續再提。

Redis 設定方式

首先確保你的機器上安裝了redis,接著在專案中設定redis連線方式:

在.env檔加上

QUEUE_CONNECTION=redis

重新整理

php artisan config:clear

Horizon 監控平台

即使我們把job都寫好了,要怎麼方便開發人員監測?有幸,市面上針對redis queue有horizon這個免費套件可以使用,我們先來看一下有什麼好玩,再來看設定方式。

Dashboard

上方是一些統計數據,下方則是顯示worker狀態

Failed Jobs面板:

你能夠在手動retry失敗的job(他背後會再新增一個新的job到queue,會分配新的horizon id,如果想判斷可以看payload的retry_of跟uuid,假設job A失敗了,被手動retry兩次,則會新增job B跟job C,B,C paylaod會記錄這是來自重試A, 然後三者的uuid都會一樣)

設定方式

安裝套件

composer require laravel/horizon

發布資產(ex:前端頁面):

php artisan horizon:install

config/horizon.php 設定檔能玩玩看

啟用

php aritisan horizon

啟用成功後就會看到狀態為active囉

權限驗證

基本上這樣就完成了,但有個很重要的是你不會希望任何人都能用GET /horizon這個api去看你有哪些job,甚至去操作你的job!

app/Providers/HorizonServiceProvider.php 檔,我們可以定義誰可以訪問這個頁面,如果你有角色的話就可以在這做使用者的角色判斷,或是權限判斷都可以,附上官方範例,可自行在改寫:

/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}

大功告成!

實驗看看

我們可以手動發起一個job,看看有沒有寫進redis當中。

進入redis-cli 執行keys * ,可以看到horizon把放進來的任務都重新包裝過了

我們在push job進redis queue的時候,horizon會把job資料重新打包成horizon版本的資料,我們來觀察一下每一個job裏面存的是什麼資料吧:

HGETALL "laravel_horizon:1630"

註解:這個1630是horizon給的id,job的獨一無二id要看uuid

如果是失敗的job會記錄:

  • exception
  • id
  • payload
  • failed_at
  • status
  • completed_at
  • reserved_at
  • queue (ex: default)
  • connection (ex: redis)
  • name (ex: “App\\Jobs\\NotifyUser”
  • updated_at, created_at

Supervisor設定

雖然我們成功將job放入redis queue了,但是你有發現任務都沒有執行嗎?

我們可以使用Supervisor 來幫我們管理worker,worker負責處理任務。

在supervisor.d資料夾下(sudo find / -type d -name “supervisor.d” 2>/dev/null 來找到位置),新增xx專案.ini設定檔:

[program:supervisor的名稱]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/your-project/artisan horizon
autostart=true
autorestart=true
numprocs=8
redirect_stderr=true
stdout_logfile=~/your-project/storage/worker.log
stopwaitsecs=3600

接著啟用supervisor:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start all

檢查是否有起動成功:

sudo supervisorctl status

踩雷經驗

active, 但是在horizon的supervisor不見了,但supervisor明明有正確運行。

解方:原來是因為沒有正確

php artisan horizon:terminate

終止在重啟後就正常吃到了

--

--

林芷蕾Alicia

Junior Backend developer in IOT company , love to share.