Gearman分布式任务处理系统(七)问题研究与性能优化

Gearman工作流程细解



一次正常的Gearman任务执行流程如上图所示:

  1. worker向Gearman Server注册自身可以执行的功能
  2. worker尝试获取一个任务
  3. server通告worker暂无任务
  4. worker通告server:“我先睡会,有活干时再叫醒我”
  5. client向server发起任务请求
  6. server唤醒可以完成这项工作的worker(可能会唤醒多个woker)
  7. worker向server发起“饥饿”请求,尝试获得一个任务
  8. server选定一个worker,将该任务分配下去
  9. 通告client:“我安排别人处理你的请求了,耐心等待吧”
  10. worker辛苦工作一段时间后,向server通告“干完了”
  11. server将结果反馈给用户

值得说明的几点

1. 任务分类:
    按优先级分:普通(SUBMIT_JOB),高(SUBMIT_JOB_HIGH),低(SUBMIT_JOB_LOW)
    按执行方式分:普通(_JOB_HIGH,_JOB_LOW),后台(_JOB_HIGH_BG,_JOB_LOW_BG)

    最大区别在于,client可以跟踪前台任务的工作状态,而不能跟踪BG任务

2. 任务工作状态的通告(worker-->server-->client):
    WORK_DATA
    WORK_WARNING
    WORK_STATUS
 对于长任务,worker应该每隔一段时间通告任务状态
    WORK_COMPLETE
    WORK_FAIL
    WORK_EXCEPTION

3. Server监控

    Gearman有“Administrative Protocol”专门用于对Gearman Server的监控,主要涉及以下几方面:

    status:所注册职能分类,worker总数目,处于工作状态的worker数目,可用worker数目等

    worker的详细信息:所注册功能、IP

    server的缓存任务最大队列长度:可以被查询也可以被设定

    该功能详见《Gearman使用》


问题分析与研究

Job Server 的单点问题
实际上 Job Server 是存在单点问题的,我们只能通过增加冗余 Job Server 的方式来解决这个问题。
我们可以通过配置域名、多个服务器配置列表等方法,在遇到其中一个 Job Server 失败时,将 Client 的请求转向另外的 Job Server。
这种情况下,Job Server 中原来存在的任务会发生丢失,除非使用了数据库或其他类似的持久化方法。

如果遇到正在连接的那个故障,由client、worker切换到好用的gearmand服务器,这样来完成high-avliable。

可以看出这里实现的并不完善,需要客户端程序自己判断,而且也可以看出并不支持几个Job Server的负载均衡调用。


Job Server 重启
Job Server重启后,worker会自动重新注册。通过 gearmand -vvvvvvv  分析发现,如果 gearmand 关闭并重新启动后,原来的 worker 会马上与 Job Server 建立连接,并询问是否有任务。
但是客户端client程序是不会自动重连的,这个要靠客户端程序中来判断重连。

使用持久队列避免任务的丢失
因为 Gearmand 的队列是放在内存中的,所以宕机或重启事件会导致队列的丢失。为了避免这种情况,可以使用持久队列,将队列存储在一个相对中立的位置。注意,持久队列只对于后台任务有效。
 
Gearman Worker 的无缝重启
我在实现Worker的过程中,采用了PHP脚本,脚本调用外部的配置文件。如果外部的配置文件修改后,需要重新启动脚本才能够使配置文件中修改的变量生效,所以想要坐到无缝的重启。即脚本Stop、Start的过程不会影响正在进行的业务。
对于无缝重启的问题,我总结了如下几个思路进行处理,解决的方法:
1、每次修改完代码后,Worker需要手工重启(先杀死然后启动)。
2、在Worker中设置,单次任务循环完成后,就对Worker进行重启。 
3、在Worker中添加一个退出函数,如果需要Worker退出的时候,在Client端发送一个优先级比较高的退出调用。 
4、在Worker中检查文件是否发生变化,如果发生了变化,退出并重启自身。 
5、为Worker编写信号控制,接受重启指令,类似于 http restart graceful 指令


多台Job Server客户端重连接问题

Gearman在多个Job Server服务器之间并不通讯,而是运行多个Job Server服务器,通过client、worker连接多个服务器。

客户端连接Job Server时,通过addJobServer或addJobServers的方式,为客户端增加多台Job Server服务器,客户端程序初始执行时不会真正的去连接所有的Job Server,只是打开连接符放到一个服务器连接列表中,当run_task时,才会真正的去连接服务器,首先向最后一台服务器发送,如果失败,客户端程序针对这笔当前的task不会自动切到另一台服务器,下一个task连接时发现这台服务器有问题就会自动切换到下个服务器,这样也是对的,因为当前这笔task的状态是不确定的,也许worker执行了返回没收到。这种情况只能是客户端程序自己判断。

为验证这种情况,自己试验一下:

在一台机上起两个gearmand(端口分别为4730和4830),在客户端把这2个gearmand都加到GearmanClient中,通过addJobServer或addJobServers的方式。

正常的,GearmanClient会将任务发送到最后添加的JobServer(4830),把job发送过去。
kill掉4830这个进程,log中报:
PHP Warning:  GearmanClient::do(): gearman_connection_read:lost connection to server
当执行下一个任务时然后自动的采用第二个JobServer,任务发送到第二个JobServer上了。

但是如果一个连接已经存在,要重复这个连接的使用,在发送Job的时候,不断的报错,不会自动切换到另一个好的JobServer上。

还有一个问题就是,这时如果前面出错那台机器又恢复使用了,客户端程序可以继续向这台机器发task,但是首次的发送会收不到应答直到超时,然后下一笔就正常了,原因需要进一步验证。

这种情况下客户端无法知道这笔task有没有被处理,这在业务上客户端必须加判断,不然一个任何有可能会被执行多次。


对background_job的异步处理的返回

待完善。。。


Gearman 性能调优 

线程模型
在大规模使用的时候,需要针对应用类型进行参数设置,以使Gearman的性能达到最优,这首先应该了解Gearman的线程模型。
 
为确保具备对海量任务调度的支持能力,Gearman毫无悬念的选择libevent作为网络操作支撑库。因此Gearman的服务器Gearmand提供了三类线程角色:
端口监听和管理线程,接受新连接请求并将之交给IO线程,1个 IO线程,完成实际的任务处理,包括命令解析,队列操作等,n个 处理线程,完成内部数据结构的管理,无系统调用尽可能简单,1个
其中第1, 3种线程对全局处理性能没有直接影响,虽然处理线程有可能成为瓶颈,但他的工作足够简单消耗可忽略不计,因此我们的性能调优主要目标是在IO线程的数量。
对每个IO线程来说,它都会有一个libevent的实例;所有Gearman的操作会以异步任务方式提交到处理线程,并由IO线程获取完成实际操作,因此IO线程的数量是与可并行处理任务数成正比。Gearmand 提供 -t 参数调整总IO线程数,需要使用 libevent 1.4 以上版本提供多线程支持。
 
进程句柄数
另外一个影响大规模部署的是进程句柄数,Gearman会为每一个注册的Worker分配一个fd(文件描述符),而这个fd的总数是受用户限制的,可以使用 ulimit -n 命令查看当前限制

flier@debian:~$ ulimit -n
 1024
flier@debian:~$ ulimit -HSn 4096 // 设置进程句柄数的最大软硬限制
4096
 
也就是说gearman缺省配置下,最多允许同时有小于1024个worker注册上来,fd用完之后的Worker和Client会出现连接超时或无响应等异常情况。因此,发生类似情况时,我们应首先检查 /proc/[PID]/fd/ 目录下的数量,是否已经超过 ulimit -n 的限制,并根据需要进行调整。而全系统的打开文件设置,可以参考 /proc/sys/fs/file-max 文件,并通过 sysctl -w fs.file-max=[NUM] 进行修改。
flier@debian:~$ cat /proc/sys/fs/file-max
24372
flier@debian:~# sysctl -w fs.file-max=100000
100000

Gearmand 本身也提供了调整句柄数量限制的功能,启动时则可以通过 –file-descriptors 参数指定,但非特权进程不能设置超过soft limit的数额。
-f [ --file-descriptors ] arg         

Number of file descriptors to allow for the process (total connections will be slightly less). Default is max allowed for user.

轮询调度 
此外,Gearmand 还提供了一些增强任务调度公平性的参数,例如 0.13 里面新增的 round-robin 模式,允许将任务公平的调度到多个 Worker,而不是用缺省按 Worker 注册函数的顺序进行调度,避免工作过于集中在少数设备上。
-R, –round-robin Assign work in round-robin order per
workerconnection. The default is to assign work in
the order of functions added by the worker.
 
Gearmand 内部通过一个 Worker 队列,在 RR 模式下动态调整 Worker 的调度次序。

 受限唤醒 
而通过 –worker-wakeup 参数,则可以指定收到任务时,需要唤醒多少个 Worker 进行处理,避免在 Worker 数量非常大时,发送大量不必要的 NOOP 报文,试图唤醒所有的 Worker。
 
-w, –worker-wakeup=WORKERS Number of workers to wakeup for each job received.
The default is to wakeup all available workers.
 
根据 Gearman 协议设计, Worker 如果发现队列中没有任务需要处理,是可以通过发送 PRE_SLEEP 命令给服务器,告知说自己将进入睡眠状态。在这个状态下,Worker 不会再去主动抓取任务,只有服务器发送 NOOP 命令唤醒后,才会恢复正常的任务抓取和处理流程。因此 Gearmand 在收到任务时,会去尝试唤醒足够的 Worker 来抓取任务;此时如果 Worker 的总数超过可能的任务数,则有可能产生惊群效应。

除此之外,针对应用特点合理使用持久化队列,在大并发任务量的情况下对性能也会有直接影响。 
归根结底,需要根据自己的应用场景,合理设计一些测试用例和自动化脚本,通过实际的运行状态进行参数调整。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值