作者hSATAC (cAt Ash)
看板PHP
标题[分享] 轻巧的 queue 系统 PHP-Resque
时间Mon Jan 16 20:07:43 2012
网志好读版:
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)
1F:推 dinos:写得真详细 01/16 21:50
2F:推 shadowjohn:谢大大无私的分享 01/17 00:09
3F:→ chrisQQ:推帅哥 cat 01/17 00:29
4F:推 appleboy46:推 01/18 17:28
5F:推 showsky:你也是乡民XD 我是Ting 01/19 22:45
6F:推 kornelius:推. Markdown 真好用 01/20 01:46
7F:推 linhomeyeu:推荐 01/26 15:34