gearman性能与应用架构设计

一、Gearman架构中的三个角色
client:    请求的发起者,工作任务的需求方(可以是C、PHP、Java、Perl、Mysql udf等等)
Job Server:请求的调度者,负责将client的请求转发给相应的worker(gearmand服务进程创建)
worker:    请求的处理者(可以是C、PHP、Java、Perl等等)

Fig-1
从上图可以看出,Gearman Client API,Gearman Worker API,Gearman Job Server都是由gearman本身提供,
我们在应用中只需要调用即可。目前client与worker api都很丰富。

二、Gearman的吞吐能力
经过的测试,结果如下:
系统环境:ubuntu-14.0.4 1个CPU 4核 2G内存 (虚拟机)
默认启动:./gearmand -d

$ cat client.php
<?php
echo "starting...", microtime(true), "\n";
$gmc = new GearmanClient();
$gmc->setCompleteCallBack(function($task){
//echo $task->data(), "\n";
});
$gmc->addServer("127.0.0.1", 4730);
for ($i = 0; $i < 100000; $i++) {
     $gmc->addTaskBackground("reserve", "just test it", null, $i);
}
$gmc->runTasks();
echo "end...", microtime(true), "\n";


$ cat worker.php
<?php
$gmw = new GearmanWorker();
$gmw->addServer("127.0.0.1", 4730);
$gmw->addFunction("reserve", function($job) {
     if ($job->unique() == 99999) {
          echo microtime(true), "\n";
     }
     return strrev($job->workload());
});
while($gmw->work());


启动一个job server实例:job server IP:127.0.0.1 PORT:4730
启动一个worker: php worker.php

worker注册reserve函数,将client的job字符串反转后返回。
client工作任务的消息为:just test it(12字节)

同步:4100/s
异步:25700/s

memcached内存准持久化的吞吐能力测试
./gearmand -d -q libmemcached —libmemcached-servers=127.0.0.1:11211
client投递100000个工作任务:16400/s

三、Gearman典型的部署结构

Fig-2

四、Gearman支持的特性
 . 高可用
   启动两个job server,他们是独立的服务进程,有各自的内存队列。
   当一个job server进程出现故障,另一个job server可以正常调度。
   (worker api与client api可以完成job server故障的切换)。

   在任何时候我们可以关闭某个worker,即使那个worker正在处理工作任务
   (Gearman不会让正在被执行的job丢失的,
   由于worker在工作时与Job server是长连接,所以一旦worker发生异常,
   Job server能够迅速感知并重新派发这个异常worker刚才正在执行的工作)

. 负载均衡(附gearman协议会详细解释)
   job server并不主动分派工作任务,而是由worker从空闲状态唤醒之后到job server主动抓取工作任务。

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

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

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

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

五、两种工作任务
1. 后台工作任务Background job——时序图

Fig-3

由图可知,client提交完job,job server成功接收后返回JOB_CREATED响应之后,client就断开与job server之间的链接了。
后续无论发生什么事情,client都是不关心的。
同样,job的执行结果client端也没办法通过Gearman消息框架获得。

2. 一般工作任务Non-background job——时序图

Fig-4
由图可知,client端在job执行的整个过程中,与job server端的链接都是保持着的,
这也给job完成后job server返回执行结果给client提供了通路。
同时,在job执行过程当中,client端还可以发起job status的查询。当然,这需要worker端的支持的。

五、关于持久化
对于队列持久化的问题,是一个值得考虑的问题。持久化必然影响高性能。
gearman支持后台工作任务的持久化,支持drizzle、mysql、memcached的持久化。

对于client提交的background job,Job server除了将其放在内存队列中进行派发之外,还会将其持久化到外部的持久化队列中。
一旦Job server发生问题重启,外部持久化队列中的background job将会被恢复到内存中,参与Job server新的派发当中。
这保证了已提交未执行的background job不会由于Job server发生异常而丢失。
并且我测试发现如果开启了持久化,那么后台工作任务会先将工作任务写到持久化介质,然后在入内存队列,再执行。
非后台工作任务,由于client与job server是保持长连接的状态,如果工作任务执行异常,client可以灵活处理,所以无须持久化。

六、Gearman框架中的一个问题
从典型部署结构看出,两个Job server之间是没有连接的。
也就是Job server间是不共享background job的。
如果通过让两个Job server指向同一个持久化队列,可以让两个Job serer互相备份。
但实际上,这样是行不通的。

因为Job server只有在启动时才会将持久化队列中的background job转入到内存队列。
也就是说,Job server1如果宕机且永远不启动,Job server2一直正常运行,
那么Job server1宕机前被提交到Job server1的未被执行的background job将永远都呆在持久化队列中,得不到执行。
另外如果多个job server实例指向同一个持久化队列,
同时重启多个job server实例会导致持久队列中的工作任务被多次载入,从而导致消息重复处理。

七、我建议的部署结构
gearman性能与应用架构设计_第1张图片
Fig-5
采用memcached做后台工作任务的准持久化队列,最好memcached和job server在内网的不同机器。
两个机器的两个服务同时挂掉的可能性比较小,同时也保证了高性能。
而且memcached应该为两个相互独立实例,防止其上述的gearman框架中的问题。
我们可以做一个监控脚本,如果某个job server异常退出,可以重启,也最大化的保证了job server的高可用。

八、总结worker的工作流程
 . Worker通过CAN_DO消息,注册到Job server上。
 . 随后发起GRAB_JOB,主动要求分派任务。
 . Job server如果没有job可分配,就返回NO_JOB。
 . Worker收到NO_JOB后,进入空闲状态,并给Job server返回PRE_SLEEP消息,告诉Job server:”如果有工作来的话,用NOOP请求我先。”
 . Job server收到worker的PRE_SLEEP消息后,明白了发送这条消息的worker已经进入了空闲态。
 . 这时如果有job提交上来,Job server会给worker先发一个NOOP消息。
 . Worker收到NOOP消息后,发送GRAB_JOB向Job server请求任务。
 . Job server把工作派发给worker。
 . Worker干活,完事后返回WORK_COMPLETE给Job server。

原文链接:
http://www.phpboy.net/2014-05/40-gearman-instruction.html

你可能感兴趣的:(gearman性能与应用架构设计)