[分享] 輕巧的 queue 系統 PHP-Resque
網誌好讀版:
http://blog.hsatac.net/2012/01/php-resque-introduction/
[Resque](https://github.com/defunkt/resque) 是 Github 基於 Redis 開
發的 background job 系統。相較其他肥大的 queue 系統, Resque 的設計
真的非常單純簡潔,充分利用 Redis 的特性。更多介紹可以看[原作者的
Blog](https://github.com/blog/542-introducing-resque)
[PHP-Resque](https://github.com/chrisboulton/php-resque) 是把
Resque porting 到 PHP 的專案。使用和 原本 Resque 一樣的概念和設計。
甚至連 Redis 的 key 命名都一樣,因此也可以使用 Ruby 版本的
[resque-web](https://github.com/defunkt/resque-web) 來監控
PHP-Resque 的運行狀況。
## 設計
Resque 的設計有兩個角色: Job 和 Worker。 每個 Job 都是定義成類別,
新增 Job 的時候會將 Job 的類別和相關參數 json_encode 後儲存到不同的
queue 裡面,而 Worker(s) 則會依序從 redis 讀取 Job 出來執行。
執行的時候並不是這個 Worker 本身去執行,而是會 fork 一個 process 來
執行。這樣設計是為了避免時間一長, Worker 的記憶體管理不良導致卡死
的狀況。
讀取 queue 時會依據你啟動 worker 的時候給的 queue 順序來讀取,因此
優先權較高的 queue 要設定在前面。 Redis 可以是單機或 RedisCluster。
而許多不同伺服器上可以按需求部屬執行不同 queue 的 worker。
以下先來介紹如何使用 PHP-Resque:
## 安裝 PHP-Resque
安裝非常容易,只要 `git clone
https://github.com/chrisboulton/php-resque.git` 下來,放到你想要的
地方,由於 Resque 沒有 config 檔的設計,設定都是寫在環境變數中再執
行就可以了。
## 環境變數
PHP-Resque 支援的環境變數有:
* QUEUE - 這個是必要的,會決定 worker 要執行什麼任務,重要的在前,
例如 `QUEUE=notify,mail,log` 。也可以設定為 `QUEUE=*` 表示執行所有
任務。
* APP_INCLUDE - 這也可以說是必要的,因為 Resque 的 Job 都是寫成物件
,那 worker 執行的時候當然要把物件的檔案引入進來。可以設成
`APP_INCLUDE=require.php` 再在 require.php 中引入所有 Job 的 Class
檔案即可。
* COUNT - 設定 worker 數量,預設是1 `COUNT=5` 。
* REDIS_BACKEND - 設定 Redis 的 ip, port。如果沒設定,預設是連
`localhost:6379` 。
* LOGGING, VERBOSE - 設定 log, `VERBOSE=1` 即可。
* VVERBOSE - 比較詳細的 log, `VVERBOSE=1` debug 的時候可以開出來看
。
* INTERVAL - worker 檢查 queue 的間隔,預設是五秒 `INTERVAL=5` 。
* PIDFILE - 如果你是開單 worker,可以指定 PIDFILE 把 pid 寫入,例如
`PIDFILE=/var/run/resque.pid` 。
有一個 Resque 支援,但 PHP-Resque 沒有的參數叫 `BACKGROUND` 可以把
resque 丟到背景執行。不過這個其實不太重要,有需要的話自己加個
`php resque.php &` 就可以了。
所以,你的指令最後可能會變這樣:
QUEUE=* APP_INCLUDE=require.php COUNT=5 VVERBOSE=1 php resque.php
如果覺得太長,可以寫一支啟動 script 來輔助你,我有寫一支可供參考:
https://gist.github.com/1619972
## 使用 PHP-Resque
把檔案抓下來以後一定想先試驗看看的,確定你的 redis-server 都有正常
啟動後,在 demo 資料夾下面有幾個檔案可以先試驗看看。
切到 demo 目錄後,執行 `VVERBOSE=1 QUEUE=* php resque.php` 應該會看
到 resque 已經開始執行了。
執行 `php queue.php PHP_Job` 、 `php queue.php Bad_PHP_Job` 、
`php queue.php Long_PHP_Job` 、 `php queue.php PHP_Error_Job` 可以
把工作丟進 queue 裡面,看看執行的結果。
後面帶的名稱其實就是 Job class 的名稱,所以 PHP-Resque 在執行時也要
把相關的 class 檔案設定在 APP_INCLUDE 引入才行。
Job 的 class 很簡單,大概長這樣:
<?php
class My_Job
{
public function perform()
{
// Work work work
echo $this->args['name'];
}
}
?>
只要定義 perform 方法, Worker 就會把 Job new 出來以後執行 perform
。
當然,也可以定義 `setUp()` 和 `tearDown()` 方法,前者會在
`perform()` 執行前執行,後者會在 `perform()` 執行後執行。
將 Job 塞入 queue 的方式是:
<?php
require_once 'lib/Resque.php';
Resque::setBackend('localhost:6379');
$args = array(
'name' => 'Chris'
);
Resque::enqueue('default', 'My_Job', $args);
?>
其中第一個參數 `default` 就是你的 queue 名稱,例如你可以設定
notify, mail, image 之類,至於為什麼要這樣設計,在後面的篇幅再敘述
。
PHP-Resque 的使用方法大致就是這樣,接下來講一些其他的小細節。
## Hooks
PHP-Resque 可以定義 Event Hooks 讓你能在相對應的事件發生時執行你想
要的動作。支援的事件有很多,請各位自行參考原專案的 README。在專案目
錄下的 extra 目錄下有 sample.plugin.php 可以看 Event hook 的範例寫
法。
有一點需要注意的是,很直覺我們會把這隻 sample.plugin.php 丟到
APP_INCLUDE 變數中,這樣沒錯,但要注意跟 enqueue 有關的 event 並不
是由 worker 來觸發,因此你在新增 Job 的那段程式也需要引入
sample.plugin.php 才能觸發到 `AFTERENQUEUE` 。
## 監控
### resque-web
前面有提到可以直接使用 resque-web 來監控 PHP-Resque 的狀態,相當建
議使用,非常清楚易懂,要看 Redis 相關的數據也可以看,不用進
redis-cli 自己打指令。
安裝方法:`gem install resque`
執行:`resque-web -p 3000` 即可運行在 3000 port。
首頁有 live reload 按鈕可以按, debug 時非常方便。
### Supervisord
在專案的 extra 目錄下另有 resque.monit 檔案,這是供
[Supervisord](http://supervisord.org/) 使用的設定檔。他會在 worker
吃掉 300MB 以上的記憶體,或者是跑了 10 次輪迴後砍掉重開。可以參考
看看。
## 佈署
之前提到可以除了預設的 default 以外,還可以設定不同的 queue,為什麼
要這樣做呢?除了執行優先權外,(撈 queue 時會按你給 worker 的設定,
在前面 queue 的會先撈,就會先執行到) 還有多伺服器部屬的原因。
假如今天你有個 queue 專門要處理使用者圖片的東西,當然一般圖片會有自
己的伺服器。於是在你的主 web 伺服器上你就可以執行
`QUEUE=notify,mail` 而在圖片伺服器上就可以執行 `QUEUE=images` 的
worker。
另外就是由於 Worker 啟動時已經將 APP_INCLUDE 的檔案都讀入,持續執行
。因此如果有修改引入的 Job 或 hook plugin 等檔案的話,deploy 時要將
worker 停止,重新啟動才會讀入新的 APP_INCLULDE 檔案。
## 已知問題
首先,PHP-Resque 使用的是
[Redisent](https://github.com/jdp/redisent) 這套 Redis interface。
但因為和另一套 php module
[phpredis](http://code.google.com/p/phpredis/) 同樣都定義了
RedisException 這個類別,所以會衝突,必須把 phpredis 移除才能使用。
再來,在部屬時常常 REDIS_BACKEND 是設到別台機器的,而且一般我們都會
開不只一個 worker ,這時候有一個已知 issue 就是有時 lpop 拉回來的
Job 錯誤,是一個陣列,導致噴出 json_decode 的錯誤,而且這個 Job 就
不會執行,會 missing 。 (see
[#32](https://github.com/chrisboulton/php-resque/issues/32))
目前還不清楚確實問題所在,不過有一個 workaround 的解法是,不要用
`COUNT=5` 去開,而是設 `COUNT=1` 然後執行 5 次,就不會有這個問題產
生。
## 結語
Resque 真的是一個很棒很輕巧的設計,感謝有人把它 porting 到 PHP 。希
望越來越多人來使用,一起來發展維護 PHP-Resque。
--
■英雄聯盟大絕使用時機■
1.生命身體受暴行脅迫,非使用大絕不能扺抗或自衛時。
2.敵方逼戰,非使用大絕不能制止時。
3.所警衛之隊友、野怪、砲塔、兵營受危害脅迫,非使用大絕不能保護時。
4.因防衛駐守之砲塔、叢林、兵營受襲擾或擅闖,非使用大絕不能制止時。
5.敵方殘血脫逃,非使用大絕不能制止時。
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 114.43.143.240
※ 編輯: hSATAC 來自: 114.43.143.240 (01/16 20:17)
推
01/16 21:50, , 1F
01/16 21:50, 1F
推
01/17 00:09, , 2F
01/17 00:09, 2F
→
01/17 00:29, , 3F
01/17 00:29, 3F
推
01/18 17:28, , 4F
01/18 17:28, 4F
推
01/19 22:45, , 5F
01/19 22:45, 5F
推
01/20 01:46, , 6F
01/20 01:46, 6F
推
01/26 15:34, , 7F
01/26 15:34, 7F
PHP 近期熱門文章
PTT數位生活區 即時熱門文章