PHP-Resque 简介
转载于: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 |
|
如果觉得太长,可以写一支啟动 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_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 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 简介的更多相关文章
- Redis数据类型简介(十分钟快速学习Redis)
如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...
- ASP.NET Core 1.1 简介
ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- Cassandra简介
在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...
- REST简介
一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...
- Microservice架构模式简介
在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...
- const,static,extern 简介
const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...
- HTTPS简介
一.简单总结 1.HTTPS概念总结 HTTPS 就是对HTTP进行了TLS或SSL加密. 应用层的HTTP协议通过传输层的TCP协议来传输,HTTPS 在 HTTP和 TCP中间加了一层TLS/SS ...
- 【Machine Learning】机器学习及其基础概念简介
机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- Cesium简介以及离线部署运行
Cesium简介 cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎,一款开源3DGIS的js库.cesium支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区 ...
随机推荐
- layer弹框,弹出后自动关闭
今天做项目,出现一个问题,需求是用ajax做文件上传功能,代码写好之后,测试发现问题. 弹出层出现以后我没有主动点击确定和关闭等操作,程序自动关闭了弹出层 一步一步排查,找到了错误,首先,先确认你页面 ...
- DLL Dynamic-Link Library Search Order
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx A system can contain ...
- SQL SERVER SQLOS的任务调度--微软亚太区数据库技术支持组 官方博客
https://blogs.msdn.microsoft.com/apgcdsd/2011/11/23/sql-server-sqlos/
- redis cluster 设置密码做集群时gem下client.rb文件修改
redis节点有设置密码,然后在创建集群的时候没有设置密码的命令 ./redis-trib.rb create --replicas 1 127.0.0.1:6381 127.0.0.1:6382 1 ...
- 第一章 在linux下python读串口 存MYSQL数据库(703N)
import MySQLdb//定义引用数据库的驱动文件 import serial import time ser = serial.Serial('/dev/ttyATH0', 115200, t ...
- cas 获取session中的用户信息
<%Object object =request.getSession().getAttribute("_const_cas_assertion_");Assertion a ...
- @MySQL中length字符长度函数使用方法
MySQL里面的length函数是一个用来获取字符串长度的内置函数,一个汉字是算三个字符,中文的标点符号也是算三个字符,一个数字或字母算一个字符.具体用法示例如下: 1.查看某字符串的长度 SELEC ...
- Linux环境MySQL集群配置
一.介绍 ======== 这篇文档旨在介绍如何安装配置基于2台服务器的MySQL集群.并且实现任意一台服务器出现问题或宕机时MySQL依然能够继续运行. 注意! 虽 然这是基于2台服务器的MySQL ...
- SRM 212 Div II Level Two: WinningRecord,Brute Force
题目来源:http://community.topcoder.com/stat?c=problem_statement&pm=3003&rd=5858 比较简单. 代码如下: #inc ...
- go语言基础之普通参数列表
1.普通参数列表 (备注:只有一个参数) 示例1: package main //必须有一个main包 import "fmt" //有参无返回值函数的定义,普通参数列表 //定义 ...