转载于:http://blog.hsatac.net/2012/01/php-resque-introduction/

Resque 是 Github 基於 Redis 开发的 background job 系统。相较其他肥大的 queue 系统, Resque 的设计真的非常单纯简洁,充分利用 Redis 的特性。更多介绍可以看原作者的 Blog

PHP-Resque 是把 Resque porting 到 PHP 的专案。使用和 原本 Resque 一样的概念和设计。甚至连 Redis 的 key 命名都一样,因此也可以使用 Ruby 版本的 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。

Resque Job 执行失败并不会自动重试,而是把它丢到 fail 的 queue 裡面。如果你有重试的需求可能要自己处理。若是有特别重要的 Job 需要监控执行状态的,可以参考 README 中的 Tracking Job Statuses 一节。

以下先来介绍如何使用 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 & 就可以了。

所以,你的指令最后可能会变这样:

1
QUEUE=* APP_INCLUDE=require.php COUNT=5 VVERBOSE=1 php resque.php

如果觉得太长,可以写一支啟动 script 来辅助你,我有写一支可供参考:

#!/bin/bash
export APP_INCLUDE=resque/require.php
export QUEUE=*
export COUNT=1
export VVERBOSE=1 # for debugging
export REDIS_BACKEND=localhost:6379
. /etc/rc.d/init.d/functions start() {
/usr/bin/php ../lib/sdk/php-resque/resque.php
} stop() {
ps -ef | grep resque | grep -v grep | grep -v resque-web | awk '{print $2}' | xargs kill -15
}
kill() {
ps -ef | grep resque | grep -v grep | grep -v resque-web | awk '{print $2}' | xargs kill -9
}
case "$1" in
start)
number=$(ps aux | grep php-resque/resque.php | grep -v grep | wc -l)
if [ $number -gt 0 ]
then
echo "php-resque is running. ($number workers)"
echo "You may wanna stop them before you start."
else
start
fi
;; stop)
stop
;; kill)
kill
;; status)
number=$(ps aux | grep php-resque/resque.php | grep -v grep | wc -l)
if [ $number -gt 0 ]
then
echo "php-resque is running. ($number workers)"
else
echo "php-resque is not running."
fi
;; *)
echo -n "Usage: $0 {start|stop|status}"
esac

使用 PHP-Resque

把档案抓下来以后一定想先试验看看的,确定你的 redis-server 都有正常啟动后,在 demo 资料夹下面有几个档案可以先试验看看。

切到 demo 目录后,执行 VVERBOSE=1 QUEUE=* php resque.php 应该会看到 resque 已经开始执行了。

执行 php queue.php PHP_Jobphp queue.php Bad_PHP_Jobphp queue.php Long_PHP_Jobphp 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 exit 后都视為正常执行,如果要让他判断失败丢到 fail queue 中的话,需要 throw exception。

将 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 之类,至於為什麼要这样设计,在后面的篇幅再叙述。

值得一提的是,在原 ruby 版 rescue 每个 Job 属於哪个 queue 是直接定义在 class 中的,PHP 版则是 enqueue 时才传入,不知道為什麼要这样设计,可能会导致一些意料之外的结果,需要注意。

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 时非常方便。

screenshot:

Supervisord

在专案的 extra 目录下另有 resque.monit 档案,这是供 Supervisord 使用的设定档。他会在 worker 吃掉 300MB 以上的记忆体,或者是跑了 10 次轮迴后砍掉重开。可以参考看看。

proctitle

如果你 ps | grep resque 只会看到 php resuqe.php 的讯息,还记得 Worker 会 fork 出一个 process 来执行 Job 吗?这样就有两倍的 processes 但是你完全分不出来哪个是哪个。原本 ruby 版本的设计是可以轻易看出 Worker fork 了哪个 process 而被 fork 出来的正在执行什麼工作。

Ruby 只要改 $0 就可以了, PHP 就没这麼简单了,要使用 setproctitle

但是这要安装 pecl 的 proctitle 模组才能使用。

安装方式:pecl install proctitle channel://pecl.php.net/proctitle-0.1.1

记得要去 php.ini 读进 .so 的 extension 。

完成后再执行 ps -e -o pid,command | grep [r]esque 时,就会从原本的

 php resque.php
php resque.php

变成好读易懂的:

 resque-1.0: Waiting for notify,mail,image,default
resque-1.0: Forked at -- ::
resque-1.0: Processing default since -- ::

佈署

之前提到可以除了预设的 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 这套 Redis interface。但因為和另一套 php module phpredis 同样都定义了 RedisException 这个类别,所以会衝突,必须把 phpredis 移除才能使用。

再来,在部属时常常 REDIS_BACKEND 是设到别台机器的,而且一般我们都会开不只一个 worker ,这时候有一个已知 issue 就是有时 lpop 拉回来的 Job 错误,是一个阵列,导致喷出 json_decode 的错误,而且这个 Job 就不会执行,会 missing 。 (see #32)

目前还不清楚确实问题所在,不过有一个 workaround 的解法是,不要用 COUNT=5 去开,而是设 COUNT=1 然后执行 5 次,就不会有这个问题產生。

结语

Resque 真的是一个很棒很轻巧的设计,感谢有人把它 porting 到 PHP 。希望越来越多人来使用,一起来发展维护 PHP-Resque。

PHP-Resque 简介的更多相关文章

  1. Redis数据类型简介(十分钟快速学习Redis)

    如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...

  2. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  3. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  4. Cassandra简介

    在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...

  5. REST简介

    一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...

  6. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  7. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  8. HTTPS简介

    一.简单总结 1.HTTPS概念总结 HTTPS 就是对HTTP进行了TLS或SSL加密. 应用层的HTTP协议通过传输层的TCP协议来传输,HTTPS 在 HTTP和 TCP中间加了一层TLS/SS ...

  9. 【Machine Learning】机器学习及其基础概念简介

    机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

  10. Cesium简介以及离线部署运行

    Cesium简介 cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎,一款开源3DGIS的js库.cesium支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区 ...

随机推荐

  1. C#中&和&&,|和||区别

    当两者都为逻辑运算符时. 其实没什么差别. &&和||当已经确定结果时,不会对第二个操作数求值.也不知道什么情况会用到这个差别.做个笔记好了. http://blog.csdn.net ...

  2. jquery-ajax请求:超时设置,增加 loading 提升体验

    前端发送Ajax请求到服务器,服务器返回数据这一过程,因原因不同耗时长短也有差别,且这段时间内页面显示空白.如何优化这段时间内的交互体验,以及长时间内服务器仍未返回数据这一问题,是我们开发中不容忽视的 ...

  3. Druid 配置_DruidDataSource参考配置

    以下是一个参考的连接池配置: <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource ...

  4. Linux命令-添加新硬盘,分区及挂载[转]

    http://www.cnblogs.com/qiyebao/p/4484370.html 转自:http://blog.chinaunix.net/uid-25829053-id-3067619.h ...

  5. 畅通工程_hdu_1232(并查集)

    畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  6. 两步改动CentOS主机名称

    在CentOS系统中,改动主机名称的过程例如以下: 1. 改动network文件 编辑network文件.配置例如以下: vi /etc/sysconfig/network 配置 NETWORKING ...

  7. [翻译] DFCircleActivityIndicator DF圆形活动状态指示器

    DFCircleActivityIndicator Native, customizable and animated circular view to show when long activity ...

  8. Oracle数据库常用函数使用--持续更新中

    NVL函数.NVL( string1, replace_with).如果string1为NULL,则NVL函数返回replace_with的值,否则返回原来的值. INSTR函数.用于查找指定字符串是 ...

  9. Python开发之AJAX全套

    概述 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用 一个简单操作需要 ...

  10. 【实践】require.js + r.js 代码打包压缩初体验

    第二个分享的是学校项目所接触到的新知识,代码压缩 + 代码打包 这次的项目用了require.js 这个插件做模块化管理的工具,所谓模块化就是在开发的过程中将功能划分成一个独立的模块,使代码可读性更强 ...