Gearman提供了一个通用的应用程序框架,用于将工做转移到更适合于工做的其余机器或流程。它容许你并行工做,负载平衡处理,并在语言间调用函数。它可用于从高可用性网站到传输数据库复制事件的各类应用程序。换句话说,它是分布式处理交流的神经系统。关于Gearman的一些优势:php
内容正在按期更新,因此请常常查看。 若是您想了解更多信息或参与其中,您可能还想查看其余沟通形式!mysql
一个Gearman驱动的应用程序由三部分组成:一个客户端,一个工做者和一个做业服务器。客户端负责建立要运行的做业并将其发送到做业服务器。做业服务器将找到能够运行做业并转发做业的合适工做人员。工做人员执行客户端请求的工做,并经过做业服务器向客户端发送响应。Gearman提供您的应用程序调用的客户端和工做者API来与Gearman做业服务器(也称为gearmand)交谈,所以您不须要处理网络或做业的映射。在内部,gearman客户端和工做者API使用TCP套接字与做业服务器进行通讯。为了更详细地解释Gearman的工做原理,咱们来看看一个简单的应用程序,它将颠倒字符串中字符的顺序。这个例子在PHP中给出,linux
咱们首先编写一个客户端应用程序,负责发送做业并等待结果,以便打印出来。它经过使用Gearman客户端API来发送一些与函数名相关的数据,在这种状况下是函数reverse
。这个代码是(为了简洁,省略了错误处理):git
<?php // Reverse Client Code $client = new GearmanClient(); $client->addServer(); print $client->do("reverse", "Hello World!");
此代码初始化一个客户端类,将其配置为使用带有add_server
(不带参数表示使用127.0.0.1
默认端口)的做业服务器 ,而后通知客户端API以reverse
工做负载“Hello world!” 运行该功能。就Gearman而言,函数名称和参数是彻底任意的,因此您能够发送适合您应用程序的任何数据结构(文本或二进制文件)。此时,Gearman客户端API将把该做业打包到一个Gearman协议数据包中,并将其发送到做业服务器以查找能够运行该reverse
功能的合适的工做人员。如今让咱们看看工人代码:github
<?php // Reverse Worker Code $worker = new GearmanWorker(); $worker->addServer(); $worker->addFunction("reverse", function ($job) { return strrev($job->workload()); }); while ($worker->work());
这段代码定义了一个函数my_reverse_function
,它接受一个字符串并返回该字符串的反转。它被一个工做对象用来注册一个函数,该函数reverse
被设置为链接到与客户端相同的本地做业服务器。看成业服务器接收到要运行的做业时,它查看已经注册了该功能名称reverse
的工做人员列表,并将该做业转发给其中一个空闲工做人员。而后,Gearman工做者API接受这个请求,运行该功能my_reverse_function
,并经过做业服务器将该功能的结果发送回客户端。sql
如您所见,客户端和工做者API(以及做业服务器)处理做业管理和网络通讯,所以您能够专一于应用程序部分。有几种不一样的方法能够在Gearman中运行做业,包括异步处理和优先做业的背景。有关详细信息,请参阅可用于各类API 的 文档。数据库
上面的例子看起来像是不少工做来运行一个函数,可是有不少方法能够用。最简单的答案是,你可使用Gearman做为用不一样语言编写的客户端和工做者之间的接口。若是您但愿PHP Web应用程序调用用C语言编写的函数,则能够将PHP客户端API与C工做程序API配合使用,并在中间粘贴做业服务器。固然,还有更高效的方式来作这件事(好比用C写一个PHP扩展),可是你可能须要一个PHP客户端和一个Python工做者,或者一个MySQL客户端和一个Perl工做者。您能够轻松地混合和匹配任何支持的语言界面,只须要全部应用程序都可以理解所发送的工做量。你最喜欢的语言是否是支持?得到参与项目,对于你或者现有的Gearman开发人员来讲,在C库的顶部放置一个语言包装可能至关容易。ubuntu
Gearman的另外一个有用的方法是把工做代码放在一个更适合工做的单独的机器上(或者一组机器上)。假设您的PHP Web应用程序想要进行图像转换,但这是在Web服务器机器上运行它的太多处理。您能够将映像发送到单独的一组工做机器上进行转换,这样负载不会影响Web服务器和其余PHP脚本的性能。经过这样作,您也能够得到一种天然形式的负载平衡,由于做业服务器仅向闲置的工做人员发送新做业。若是在特定机器上运行的全部工做人员都很忙,则没必要担忧在那里发送新工做。这使得多核服务器的扩展很是简单:工人机器上有16个核心吗?启动你的工人的16个实例(或更多,若是他们没有CPU绑定)。也能够无缝添加新机器来扩展您的工做池,只需启动它们,安装工做代码,并将它们链接到现有的做业服务器。设计模式
如今你可能会问若是做业服务器死亡?您能够运行多个做业服务器,并让客户端和工做人员链接到配置的第一个可用做业服务器。这样,若是一个工做服务器死亡,客户端和工做人员会自动故障转移到另外一个工做服 你可能不想运行太多的做业服务器,但有两到三个是冗余的好主意。左图显示了一个简单的Gearman集群的外观。api
从这里,您能够根据须要扩展您的客户和工做人员。做业服务器能够轻松处理一次链接数百个客户端和工做人员。您能够在容量容许的状况下绘制本身的物理(或虚拟)机器生产线,可能将负载分配到任意数量的机器。有关具体使用和安装的更多细节,请参阅示例部分。
更新来。
client:请求的发起者,工做任务的需求方(能够是C、PHP、Java、Perl、Mysql udf等等)
Job Server:请求的调度者,负责将client的请求转发给相应的worker(gearmand服务进程建立)
worker:请求的处理者(能够是C、PHP、Java、Perl等等)
从上图能够看出,Gearman Client API,Gearman Worker API,Gearman Job Server都是由gearman自己提供,咱们在应用中只须要调用便可。目前client与worker api都很丰富。
通过的测试,结果以下:
系统环境:ubuntu-14.0.4 1个CPU 4核 2G内存 (虚拟机)
默认启动:./gearmand -d
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"; 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
启动两个job server,他们是独立的服务进程,有各自的内存队列。当一个job server进程出现故障,另外一个job server能够正常调度。(worker api与client api能够完成job server故障的切换)。在任什么时候候咱们能够关闭某个worker,即便那个worker正在处理工做任务(Gearman不会让正在被执行的job丢失的,因为worker在工做时与Job server是长链接,因此一旦worker发生异常,Job server可以迅速感知并从新派发这个异常worker刚才正在执行的工做)
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上。
后台工做任务Background job——时序图
由图可知,client提交完job,job server成功接收后返回JOB_CREATED响应以后,client就断开与job server之间的连接了。后续不管发生什么事情,client都是不关心的。一样,job的执行结果client端也没办法经过Gearman消息框架 得到。
通常工做任务Non-background job——时序图
由图可知,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能够灵活处理,因此无须持久化。
从典型部署结构看出,两个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实例会致使持久队列中的工做任务被屡次载入,从而致使消息重复处理。
采用memcached作后台工做任务的准持久化队列,最好memcached和job server在内网的不一样机器。两个机器的两个服务同时挂掉的可能性比较小,同时也保证了高性能。并且memcached应该为两个相互独立实例,防止其上述的gearman框架中的问题。咱们能够作一个监控脚本,若是某个job server异常退出,能够重启,也最大化的保证了job server的高可用。
目前有一个如今的管理工具,https://github.com/brianlmoon/GearmanManager,可是只支持php-5.2,不过能够自行修改支持php-5.4,我建议若是使用PHP做为worker进程,使用php-5.4以上的版本。该工具的设计方法能够借鉴,能够比较好的管理gearman worker。
结合linux crontab,php脚本负责产生job,将任务分发到多台服务器周期性的并发执行。能够取代目前咱们比较多的crontab的工做任务。
邮件短信发送
异步log
跨语言相互调用(对于密集型计算的需求,能够用C实现,PHP直接调用)
其余耗时脚本
$>wget https://launchpadlibrarian.net/165674261/gearmand-1.1.12.tar.gz
sudo apt-get install libboost1.55-all-dev gperf libevent libevent-dev uuid libmemcached-dev tar zxvf gearmand-1.1.12.tar.gz cd gearmand-1.1.12 /configure --prefix=/home/phpboy/Server/gearman make & make install
a)默认启动
./gearman -d
b)支持memcached准持久化
./gearmand -d -q libmemcached --libmemcached-servers=127.0.0.1:11211
wget http://pecl.php.net/get/gearman-1.1.2.tgz tar zxvf gearman-1.1.2.tgz#cd gearman-1.1.2 phpize ./configure --with-php-config=php-config make & make install
能够用上面个人测试的示例
附Gearmand(job server的启动参数简单说明)
-b, –backlog=BACKLOG 链接请求队列的最大值
-d, –daemon Daemon 守护进程化
-f, –file-descriptors=FDS 可打开的文件描述符数量
-h, –help
-l, –log-file=FILE Log 日志文件
-L, –listen=ADDRESS 开启监听的地址
-p, –port=PORT 开启监听的端口
-P, –pid-file=FILE File pid file
-r,–protocol=PROTOCOL 使用的协议
-q, –queue-type=QUEUE 持久化队列类型
-t, –threads=THREADS I/O线程数量
-u, –user=USER 进程的有效用户名
libdrizzle Options:
--libdrizzle-host=HOST Host of server.
--libdrizzle-port=PORT Port of server.
--libdrizzle-uds=UDS Unix domain socket for server.
--libdrizzle-user=USER User name for authentication.
--libdrizzle-password=PASSWORD Password for authentication.
--libdrizzle-db=DB Database to use.
--libdrizzle-table=TABLE Table to use.
--libdrizzle-mysql Use MySQL protocol.
libmemcached Options:
--libmemcached-servers=SERVER_LIST List of Memcached servers to use.
libsqlite3 Options:
--libsqlite3-db=DB Database file to use.
--libsqlite3-table=TABLE Table to use.
libpq Options:
--libpq-conninfo=STRING PostgreSQL connection information string.
--libpq-table=TABLE Table to use.
http Options:
--http-port=PORT Port to listen on.
Gearman工做在TCP上,默认端口为4730,client与job server、worker与job server的通讯都基于此tcp的socket链接。client是工做任务的发起者,worker是能够注册处理函数的工做任务执行者,job server为工做的调度者。协议包含请求报文与响应报文两个部分,全部发向job server的数据包(TCP报文段的数据部分)认为是请求报文,全部从job server发出的数据包(TCP报文段的数据部分)认为是响应报文。worker或者client与job server间的通讯是基于二进制数据流的,但在管理client也有基于行文本协议的通讯。
请求报文与响应报文是由二进制包封装。一个二进制包由头header和可选的数据部分data组成。
报文类别,请求报文或者响应报文,4个字节
“�REQ” 请求报文
“�RES” 响应报文
包类型,高(大)字节序(网络字节序),4个字节可能的类型有
类型值 名称 报文类型 发送者
1 CAN_DO REQ Worker
2 CANT_DO REQ Worker
3 RESET_ABILITIES REQ Worker
4 PRE_SLEEP REQ Worker
5 (unused) - -
6 NOOP RES Worker
7 SUBMIT_JOB REQ Client
8 JOB_CREATED RES Client
9 GRAB_JOB REQ Worker
10 NO_JOB RES Worker
11 JOB_ASSIGN RES Worker
12 WORK_STATUS REQ Worker
13 WORK_COMPLETE REQ Worker
14 WORK_FAIL REQ Worker
15 GET_STATUS REQ Client
16 ECHO_REQ REQ Client/Worker
17 ECHO_RES RES Client/Worker
18 SUBMIT_JOB_BG REQ Client
19 ERROR RES Client/Worker
20 STATUS_RES RES Client
21 SUBMIT_JOB_HIGH REQ Client
22 SET_CLIENT_ID REQ Worker
23 CAN_DO_TIMEOUT REQ Worker
24 ALL_YOURS REQ Worker
25 WORK_EXCEPTION REQ Worker
26 OPTION_REQ REQ Client/Worker
27 OPTION_RES RES Client/Worker
28 WORK_DATA REQ Worker
29 WORK_WARNING REQ Worker
30 GRAB_JOB_UNIQ REQ Worker
31 JOB_ASSIGN_UNIQ RES Worker
32 SUBMIT_JOB_HIGH_BG REQ Client
33 SUBMIT_JOB_LOW REQ Client
34 SUBMIT_JOB_LOW_BG REQ Client
35 SUBMIT_JOB_SCHED REQ Client
36 SUBMIT_JOB_EPOCH REQ Client
数据部分,数据部分的各个部分为null字符分隔。
当job server收到此包类型的请求报文时,就简单的产生一个包类型为ECHO_RES,同时将请求报文的数据部分做为响应报文的数据部分的报文。主要用于测试或者调试
如:
Client -> Job Server 00 52 45 51 0REQ 报文类型 00 00 00 a0 16 (Packet type: ECHO_ERQ) 00 00 00 04 4 (Packet length) 74 65 73 74 test (Workload)
当job server响应ECHO_REQ报文时发送的包类型为ECHO_RES的响应报文
如:
Job Server -> Client 00 52 45 53 0RES 报文类型 00 00 00 a1 17 (Packet type: ECHO_ERS) 00 00 00 04 4 (Packet length) 74 65 73 74 test (Workload)
client发送的请求报文:(仅能由client发送的请求报文)
SUBMIT_JOB, SUBMIT_JOB_BG,SUBMIT_JOB_HIGH, SUBMIT_JOB_HIGH_BG,SUBMIT_JOB_LOW, SUBMIT_JOB_LOW_BG
当client有一个工做任务须要运行,就会提交相应的请求报文,job server响应包类型为JOB_CREATED数据部分为job handle的响应报文。SUBMIT_JOB为普通的工做任务,client获得状态更新及通知任务已经完成的响应;SUBMIT_JOB_BG为异步的工做任务,client不关心任务的完成状况;SUBMIT_JOB_HIGH为高优先级的工做任务,SUBMIT_JOB_HIGH_BG为高优先级的异步任务;SUBMIT_JOB_LOW为低优先级的工做任务,SUBMIT_JOB_LOW_BG为低优先级的异步任务。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13
Client -> Job Server 00 52 45 51 0REQ (报文类型) 00 00 00 07 7 (Packet type: SUBMIT_JOB) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (F
和SUBMIT_JOB_BG相似,此类型的工做任务不会当即执行,而在设置的某个时间运行。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Client -> Job Server 00 52 45 51 0REQ (报文类型) 00 00 00 23 35 (Packet type: SUBMIT_JOB_SCHED) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (Function) 00 � (Unique ID) 01 � (minute 0-59) 01 � (hour 0-23) 01 � (day of month 1-31) 01 � (day of month 1-12) 01 � (day of week 0-6) 74 65 73 74 test (Workload)
和SUBMIT_JOB_SCHED做用同样,只是将设置的时间定为了uinx时间戳GET_STATUS获取某个工做任务执行的状态信息
设置client与job server链接的选项
client获取的响应报文:
JOB_CREATED响应包类型为SUBMIT_JOB*的请求报文,数据部分为job handle
WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,WORK_FAIL, WORK_EXCEPTION
对于后台运行的工做任务,任务执行信息能够经过包类型为上面的值来查看。
响应包类型为GET_STATUS的请求报文,一般用来查看一个后台工做任务是否已经完成,以及完成的百分比。
响应包类型为OPTION_REQ的请求报文
worker发送的请求报文:
通知job server能够执行给定的function name的任务,此worker将会放到一个链表,当job server收到一个function name的工做任务时,worker为被唤醒。
和CAN_DO相似,只是针对给定的function_name的任务设置了一个超时时间。
worker通知job server已经不能执行给定的function name的任务
worker通知job server不能执行任何function name的任务
worker通知job server它将进入sleep阶段,而以后此worker会被包类型为NOOP的响应报文唤醒。
worker向job server抓取工做任务,job server将会响应NO_JOB或者JOB_ASSIG
和GRAB_JOB相似,可是job server在有工做任务时将会响应JOB_ASSIGN_UNIQ
worker请求报文的数据部分更新client
worker请求报文表明一个warning,它应该被对待为一个WARNING
Sworker更新某个job handle的工做状态,job server应该储存这些信息,以便响应以后client的GET_STATUS请求
通知job server及全部链接的client,数据部分为返回给client的数据
通知job server及全部链接的client,工做任务执行失败
通知job server及全部链接的client,工做任务执行失败并给出相应的异常
设置worker ID,从而job server的控制台及报告命令能够标识各个worker,数据部分为worker实例的标识
暂未实现
job server唤醒sleep的worker,以即可以开始抓取工做任务
job server响应GRAB_JOB的请求,通知worker没有等待执行的工做任务
job server响应GRAB_JOB的请求,通知worker有须要执行的工做任务
job server响应GRAB_JOB_UNIQ的请求,和JOB_ASSIGN同样,只是为client传递了一个惟一标识
基于上述的协议描述一个完整的例子
worker注册能够执行的工做任务
Worker -> Job Server
1 2 3 4 5 6 7 |
00 52 45 51 0REQ (Magic) 00 00 00 01 1 (Packet type: CAN_DO) 00 00 00 07 7 (Packet length) 72 65 76 65 72 73 65 reverse (Function) |
worker检测或者抓取工做任务
1 2 3 4 5 6 7 |
Worker -> Job Server 00 52 45 51 0REQ (Magic) 00 00 00 09 9 (Packet type: GRAB_JOB) 00 00 00 00 0 (Packet length) |
job server响应worker的抓取工做(没有工做任务)
1 2 3 4 5 |
00 52 45 53 0RES (Magic) 00 00 00 0a 10 (Packet type: NO_JOB) 00 00 00 00 0 (Packet length) |
worker通知job server开始sleep
1 2 3 4 5 |
00 52 45 51 0REQ (Magic) 00 00 00 04 4 (Packet type: PRE_SLEEP) 00 00 00 00 0 (Packet length) |
client提交工做任务
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Client -> Job Server 00 52 45 51 0REQ (Magic) 00 00 00 07 7 (Packet type: SUBMIT_JOB) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (Function) 00 � (Unique ID) 74 65 73 74 test (Workload) |
job server响应client的SUBMIT_JOB请求,返回job handle
1 2 3 4 5 6 7 |
00 52 45 53 0RES (Magic) 00 00 00 08 8 (Packet type: JOB_CREATED) 00 00 00 07 7 (Packet length) 48 3a 6c 61 70 3a 31 H:lap:1 (Job handle) |
job server唤醒worker
1 2 3 4 5 6 7 |
Job Server -> Worker 00 52 45 53 0RES (Magic) 00 00 00 06 6 (Packet type: NOOP) 00 00 00 00 0 (Packet length) |
worker的抓取工做任务
job server分配工做任务给worker
worker完成工做任务通知job server
job server通知client完成了工做任务
Job Server -> Client
每一个client与job server是全双工通讯,在一个socket能够完成多个工做任务的投递,可是收到任务的执行结果的顺序可能与投递的顺序不一致。
详见:http://gearman.org/protocol/
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。