PHP利用Gearman来处理并行多进程问题

最近工作中开发的一套系统,其中很多都是需要操作多服务器的,比如需要同时发布数据到2000个服务器上,或者同时向2000个服务器拉取数据。刚开始的解决方案就是单纯用PHP的curl_multi的方式并发处理请求,而且参考了淘宝技术博客的《Rolling cURL: PHP并发最佳实践》,但是由于网络和数据以及各个服务器等等的一些情况导致这种并发处理的响应时间很慢,因为在并发请求的过程中还包括记录日志,处理数据等逻辑,等待处理结果并返回,所以也不能友好的满足后台操作的体验。

现在重新设计一种方案,利Gearman来实现并发的需求。通过Client将请求发送到Gearman的Jobs,在每个Work中来再来进行curl_multi和数据处理和日志等一些操作,同时用Supervisor来监控Gearman以及Works的进程,这样可以实现一个并行的多进程和负载均衡的方案。

Gearman可以做什么

  • 异步处理:图片处理,订单处理,批量邮件/通知之类的
  • 要求高CPU或内存的处理:大容量的数据处理,MapReduce运算,日志聚集,视频编码
  • 分布式和并行的处理
  • 定时处理:增量更新,数据复制
  • 限制速率的FIFO处理
  • 分布式的系统监控任务

Gearman工作原理
使用Gearman的应用通常有三部分组成:一个Client、一个Worker、一个 任务服务器。 Client的作用是提出一个 Job 任务 交给 Job Server 任务服务器。Job Server 会去寻找一个 合适的 Worker 来完成这项任务。Worker 执行由 Client 发送过来的 Job,并且将结果通过 Job Server 返回给 Client。Gearman 提供了 Client 和 Worker 的 API,利用这些API 应用可以同 Gearman Job Server来进行通信。Gearman 内部 Client 和 Worker 之间的通信都是通过 TCP 连接来进行的。

stackGearman可以将工作的负载分担到不同的机器中。

cluster

安装配置
我只是记录下我安装配置的过程,我在Ubuntu和CentOS中都试了下。
CentOS YUM 安装

Ubuntu apt 安装

1 apt-getinstallgearman

源码编译

1 yuminstalluuid-devel libuuid libuuid-devel uuid boost-devel libevent libevent-devel
2 wget -c https://launchpad.net/gearmand/1.2/1.1.7/+download/gearmand-1.1.7.tar.gz
3 tarzxvf gearmand-1.1.7.tar.gz
4 ./configure --prefix=/usr/local/gearmand
5 make&&makeinstall

安装好以后启动

1 gearmand -d

加上-d参数是表示后台运行,你可以gearmand -h 来查看其它的选项,启动的时候带上其它配置参数

1 /usr/sbin/gearmand --pid-file=/var/run/gearman/gearmand.pid --user=gearman --daemon --log-file=/var/log/gearman-job-server/gearman.log --listen=127.0.0.1

安装PHP Gearman扩展
我都是用pcel来安装的,你也可以下载源码包来编译安装,但是记得要先安装libgearmanre2c,不然扩展编译安装会出错。

01 peclinstallgearman#不成功并提示版本问题可以试试 pecl install gearman-1.0.3,默认好像是1.1.2
02 1
03 编译安装也很简单
04 1
06 tarzxvf gearman-1.1.1.tgz
07 phpize
08 ./configure
09 make&&makeinstall
10 echo"extension=gearman.so">> /etc/php.ini

PHP接口函数
Gearman提供很多完善的扩展函数,包括GearmanClient,GearmanJob,GearmanTask,GearmanWorker,具体可以查看PHP官方手册.
这是官方提供的Example其中的一个,相当与一个并发的分发任务处理的例子
gearman_client.php

01 <?php
02  
03 $client=newGearmanClient();
04 $client->addServer();
05  
06 // initialize the results of our 3 "query results" here
07 $userInfo=$friends=$posts= null;
08  
09 // This sets up what gearman will callback to as tasks are returned to us.
10 // The $context helps us know which function is being returned so we can
11 // handle it correctly.
12 $client->setCompleteCallback(function(GearmanTask$task,$context)use(&$userInfo, &$friends, &$posts) {
13 switch($context)
14 {
15 case'lookup_user':
16 $userInfo=$task->data();
17 break;
18 case'baconate':
19 $friends=$task->data();
20 break;
21 case'get_latest_posts_by':
22 $posts=$task->data();
23 break;
24 }
25 });
26  
27 // Here we queue up multiple tasks to be execute in *as much* parallelism as gearmand can give us
28 $client->addTask('lookup_user','joe@joe.com','lookup_user');
29 $client->addTask('baconate','joe@joe.com','baconate');
30 $client->addTask('get_latest_posts_by','joe@joe.com','get_latest_posts_by');
31  
32 echo"Fetching...\n";
33 $start= microtime(true);
34 $client->runTasks();
35 $totaltime= number_format(microtime(true) - $start, 2);
36  
37 echo"Got user info in: $totaltime seconds:\n";
38 var_dump($userInfo,$friends,$posts);

gearman_work.php

01 <?php
02  
03 $worker=newGearmanWorker();
04 $worker->addServer();
05  
06 $worker->addFunction('lookup_user',function(GearmanJob$job) {
07 // normally you'd so some very safe type checking and query binding to a database here.
08 // ...and we're gonna fake that.
09 sleep(3);
10 return'The user requested (' . $job->workload() . ') is 7 feet tall and awesome';
11 });
12  
13 $worker->addFunction('baconate',function(GearmanJob$job) {
14 sleep(3);
15 return'The user (' . $job->workload() . ') is 1 degree away from Kevin Bacon';
16 });
17  
18 $worker->addFunction('get_latest_posts_by',function(GearmanJob$job) {
19 sleep(3);
20 return'The user (' . $job->workload() . ') has no posts, sorry!';
21 });
22  
23 while($worker->work());

我在3个终端中都执行了gearman_work.php

1 ryan@ryan-lamp:~$psaux | grepgearman* | grep-vgrep
2 gearman 1504 0.0 0.1 60536 1264 ? Ssl 11:06 0:00 /usr/sbin/gearmand --pid-file=/var/run/gearman/gearmand.pid --user=gearman --daemon --log-file=/var/log/gearman-job-server/gearman.log --listen=127.0.0.1
3 ryan 2992 0.0 0.8 43340 9036 pts/0 S+ 14:05 0:00 php /var/www/gearmand_work.php
4 ryan 3713 0.0 0.8 43340 9036 pts/1 S+ 14:05 0:00 php /var/www/gearmand_work.php
5 ryan 3715 0.0 0.8 43340 9036 pts/2 S+ 14:05 0:00 php /var/www/gearmand_work.php

来查看下执行gearman_work.php的结果shell

1 Fetching...
2 Got user info in: 3.03 seconds:
3 string(59)"The user requested (joe@joe.com) is 7 feet tall and awesome"
4 string(56)"The user (joe@joe.com) is 1 degree away from Kevin Bacon"
5 string(43)"The user (joe@joe.com) has no posts, sorry!"

看到上面的3.03 seconds,说明client请求过去的任务被并行分发执行了。
在实际的生产环境中,为了监测gearmand和work的进程没有被意外退出,我们可以借助Supervisor这个工具,下次我再单独来写个Supervisor的笔记。


########################

准备工作已经完毕,试验开始
1、启动job
gearmand -d

2、启动worker
php -c /etc/php5/apache2/php.ini worker.php

3、启动client(新开终端中打开)
php -c /etc/php5/apache2/php.ini client.php

屏幕显示字符串的长度 “5”

这里,有几点需要说明一下:
1、这里直接用php cli方式运行,添加-c参数是为了加载php.ini配置文件,以加载gearman扩展
2、worker应该做成守护进程(CLI模式),可以开启多个,这样client发起的任务就会分发到各个worker分别来执行(自动负载均衡 )
这个例子由于太过简单,即使开启多个worker也无法看出效果,不过可以通过终止其中一个,可以看出系统自动切换到其他worker继续正常执行
3、同理,client也是可以开启多个的(模型请参考之前的那边日志)

4、同时,job也可以开启多个,以避免单点故障


########################################################################


从上图可以看出,gearman支持的特性:

1.高可用

启动两个job server,他们是独立的服务进程,有各自的内存队列。当一个job server进程出现故障,另一个job server可以正常调度。(worker api与client api可以完成job server故障的切换)。在任何时候我们可以关闭某个worker,即使那个worker正在处理工作任务(Gearman不会让正在被执行的job丢失的,由于worker在工作时与Job server是长连接,所以一旦worker发生异常,Job server能够迅速感知并重新派发这个异常worker刚才正在执行的工作

2.负载均衡(附gearman协议会详细解释)

job server并不主动分派工作任务,而是由worker从空闲状态唤醒之后到job server主动抓取工作任务。

3.可扩展

松耦合的接口和无状态的job,只需要启动一个worker,注册到Job server集群即可。新加入的worker不会对现有系统有任何的影响。

4.分布式

gearman是分布式的任务分发框架,worker与job server,client与job server通信基于tcp的socket连接。

5.队列机制

gearman内置内存队列,默认情况队列最大容量为300W,可以配置最大支持2^32-1,即4 294 967 295。

6.高性能

作为Gearman的核心,Job server的是用C/C++实现的,由于只是做简单的任务派发,因此系统的瓶颈不会出在Job server上。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值