CentOS的Gearman安装与使用无错版

分类:技术文档 - 队列 | 阅读(3600) | 发布于:2014-03-07 09:23

CentOS的Gearman安装与使用无错版

官网: http://gearman.org/
通常,多语言多系统之间的集成是个大问题,一般来说,人们多半会采用WebService的方式来处理此类集成问题,
但不管采用何种风格的WebService,如RPC风格,或者REST风格,其本身都有一定的复杂性。
相比之下,Gearman也能实现类似的作用,而且更简单易用。
一个Gearman请求的处理过程涉及三个角色:Client -> Job -> Worker。
Client:请求的发起者,可以是C,PHP,Perl,MySQL UDF等等。
Job:请求的调度者,用来负责协调把Client发出的请求转发给合适的Work。
Worker:请求的处理者,可以是C,PHP,Perl等等。
因为Client,Worker并不限制用一样的语言,所以有利于多语言多系统之间的集成。
甚至我们通过增加更多的Worker,可以很方便的实现应用程序的分布式负载均衡架构。
下面看看如何安装运行一个例子,条件所限,我们把Client,Job,Worker三个角色运行在一台服务器上:

安装libevent:

	wget http://www.monkey.org/~provos/libevent-1.4.12-stable.tar.gz

	tar zxvf libevent-1.4.12-stable.tar.gz

	cd libevent-1.4.12-stable/

	./configure --prefix=/usr

	make && make install

	/sbin/ldconfig

	cd ../

安装Gearman server and library: gearmand版本不要太高了(装完后gearmand启动不了)

	wget https://launchpad.net/gearmand/1.0/0.14/+download/gearmand-0.14.tar.gz

	tar zxvf gearmand-0.14.tar.gz

	cd gearmand-0.14

	./configure
	make && make install

	/sbin/ldconfig

	cd ../

安装Gearman PHP extension:此处gearman的版本不要太高了

	wget http://pecl.php.net/get/gearman-0.8.3.tgz

	tar -zxvf gearman-0.8.3.tgz

	cd gearman-0.8.3

	/usr/local/php/bin/phpize
	./configure --with-php-config=/usr/local/php/bin/php-config --with-gearman

	make

	make install

	cd ../


编辑php.ini配置文件加载相应模块并使之生效:
extension=gearman.so

启动Job:
gearmand -d

如果当前用户是root的话,则需要这样操作:
gearmand -d -u root

缺省会使用4730端口,下面会用到。
注意:如果找不到gearmand命令的路径,别忘了用whereis gearmand确认。
我们可以用 ps 指令來查看启动是否成功:
ps aux |grep gearmand

编写Worker: worker.php文件内容如下:
<php

	$worker= new GearmanWorker();

	$worker->addServer('127.0.0.1', 4730);

	$worker->addFunction('reverse', 'my_reverse_function');

	while ($worker->work());

	function my_reverse_function($job){

	return strrev($job->workload());

	}

?>

设置后台运行work:
php /var/www/html/worker.php &

编写Client:
client.php文件内容如下:

<php

	$client= new GearmanClient();

	$client->addServer('127.0.0.1', 4730);

	echo $client->do('reverse', 'Hello World!'), "\n";

?>

运行client:
php /var/www/html/client.php

输出:!dlroW olleH


首先, PHP Gearman Extension 提供了一个名为GearmanClient 的类别,它可以让程序安排工作给 Job Server 。
而 addServer 方法表示要通知的是哪些 Job Server ,也就是说如果有多台 Job Server 的话,就可以透过 addServer 新增。
然后我们将要呼叫哪个 Worker 以及该 Worker 所需要的数据,利用 GearmanClient 的doBackground 方法传送过去。
doBackground 方法顾名思义就是在背景执行, Client 在丢出需求后就可以继续处理其他的程序。
$client->doBackground('sendEmail', serialize($emailData));
echo "Email sending is done.\n";

doBackground
方法的第一个参数是告诉 Job Server 要执行哪个功能,而这个功能则是由 Worker 提供的;
要注意是,这个参数只是识别用的,并不是真正的函式名称。而第二个参数是要传给 Worker 的数据,
它必须是个字符串;因此如果要传送的是数组的话,我们就要用 PHP 的 serialize 函式来对这些资料做串行化。



实例

client:

<?php
$client = new GearmanClient();
$client->addServer('127.0.0.1', 4730);//本机可以直接addServer(),默认服务器端使用4730端口
$client->setCompleteCallback('completeCallBack');//先绑定才有效

$result1 = $client->do('say', 'do');//do是同步进行,进行处理并返回处理结果。
$result2 = $client->doBackground('say', 'doBackground');//异步进行,只返回处理句柄。
$result3 = $client->addTask('say', 'addTask');//添加任务到队列,同步进行?通过添加task可以设置回调函数。
$result4 = $client->addTaskBackground('say', 'addTaskBackground');//添加后台任务到队列,异步进行?
$client->runTasks();//运行队列中的任务,只是do系列不需要runTask()。

echo 'result1:';
var_dump($result1);
echo '<br/>';

echo 'result2:';
var_dump($result2);
echo '<br/>';

echo 'result3:';
var_dump($result3);
echo '<br/>';

echo 'result4:';
var_dump($result4);
echo '<br/>';

//绑定回调函数,只对addTask有效
function completeCallBack($task)
{
	echo 'CompleteCallback!handle result:'.$task->data().'<br/>';
}

worker:

<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('say', function(GearmanJob $job){
	$workload = $job->workload();//接收client传递的数据
	echo 'receive data:'.$workload.PHP_EOL;
	return strrev($workload);//仅作反转处理
});

//无际循环运行,gearman内部已有处理,不会出现占用过高死掉的情况
while($worker->work()){
	if($worker->returnCode() !== GEARMAN_SUCCESS){
		echo 'error'.PHP_EOL;
	}
}
以上client输出:
CompleteCallback!handle result:ksaTdda
result1:string(2) “od”
result2:string(17) “H:iZ943bixttyZ:87″
result3:object(GearmanTask)#2 (0) { }
result4:object(GearmanTask)#3 (0) { }
worker输出:
receive data:do
receive data:doBackground
receive data:addTaskBackground
receive data:addTask



pcntl扩展实现粗略的多worker守护:
由于worker要长驻后台时刻准备着被jobserver调用来处理job,所以worker不能死掉。
网上有的解决办法是通过定时任务进行重启worker,这应该是不错的方案。
也有说进行多进程守护,但实际上php比较难实现,
通过pcntl扩展是其中一种方案,主进程forck出来的子进程来启动运行worker,相当于worker作为主进程的子进程。
主进程监护着子进程,worker死掉及时启动新的一个。
但如果主进程死掉呢?由于主进程不进行什么业务处理,死掉的概率要比子进程worker死掉的概率要小不少吧。

以下示例,主进程启动5个子进程,也就是开启5个worker,并监护着它们:

<?php
declare (ticks = 1);
$num = 5;//最大子进程数
$child = 0;//当前子进程数

//信号处理函数
function sig_handler($sig)
{
	global $child;
	switch ($sig) {
		case SIGCHLD:
			$child--;
			echo 'SIGCHLD received! now we have '.$child.' process'.PHP_EOL;
			break;
		case SIGINT:
			$child--;
			echo 'SIGINT received! now we have '.$child.' process'.PHP_EOL;
			break;
		case SIGTERM:
			$child--;
			echo 'SIGTERM received! now we have '.$child.' process'.PHP_EOL;	
			break;
		default:
			# code...
			break;
	}
}

//安装信号处理器
pcntl_signal(SIGTERM, "sig_handler");//进程被kill时发出的信号
// pcntl_signal(SIGHUP,  "sig_handler");//终端关闭时发出的信号
pcntl_signal(SIGINT, "sig_handler");//中断进程信号,如Ctrl+C
pcntl_signal(SIGCHLD, "sig_handler");//进程退出信号

while(true)
{
	$child++;
	$parentpid = getmypid();
	$pid = pcntl_fork();//一分为二,父进程和子进程都会执行以下代码
	if($pid == -1)
	{
		exit("can not fork!");//出错
	}else if($pid > 0){
		//父进程处理代码
		echo 'I am parent.my pid is'.$pid.' and my parent pid is'.$parentpid.PHP_EOL;
		if($child >= $num)
		{
			pcntl_wait($status);//挂起,while语句不会继续执行。等待子进程结束,防止子进程成为僵尸进程
		}
	}else if($pid == 0){
		//子进程代码
		echo 'I am child, and my parent pid is '.$parentpid." my pid is ".getmypid()." now have $child process".PHP_EOL;		
		//执行具体代码
		pcntl_exec('/usr/bin/php', array('/var/www/test/mywork.php'));

	}
	pcntl_signal_dispatch();//分发信号,使安装的信号处理器能接收。
	//低于php5.3该函数无效,但有开头的declare (ticks = 1);表示每执行一条低级指令,
	//就检查一次信号,如果检测到注册的信号,就调用其信号处理器
	sleep(rand(3,5));//防止100%占用
}
出于方便的考虑,Worker,Client使用的都是PHP,但这并不影响演示,实际应用中,你完全可以通过Gearman集成不同语言实现的 Worker,Client。
或许此时你还想了解前面提到的负载均衡功能:很简单,只要增加多个Worker即可,
你可以按照worker.php的样子 多写几个类似的文件,并设置不同的返回值用以识别演示效果。
然后依次启动这几个Worker文件,并多次使用client.php去请求,你就会发现 Job会把Client请求转发给不同的Worker。
以守护进程启动
gearmand -L 192.168.0.1 -p 4730 -u root -d

命令行工具
如果你觉得安装PHP之类的东西太麻烦的话,你也可以仅仅通过命令行工具来体验Gearman的功能:
启动Worker:
gearman -w -f wc -- wc -l &

运行Client:
gearman -f wc < /etc/passwd

具体可以参考官方文档,还有一些不错的PDF。
http://docs.php.net/manual/zh/book.gearman.php
http://www.ibm.com/developerworks/cn/opensource/os-php-gearman/index.html
也可以参考此文:http://www.jaceju.net/blog/archives/1211
分布式基础学习【一】 —— 分布式文件系统
http://www.cnblogs.com/duguguiyu/archive/2009/02/22/1396034.html
分布式基础学习【二】 —— 分布式计算系统(Map/Reduce)
http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html
淘宝网Hadoop专家
http://coderplay.javaeye.com/

标签:gearman异步队列