当前位置:首页 > PHP教程 > php高级应用 > 列表

高性能web站点分布式处理系统gearman应用实例

发布:smiling 来源: PHP粉丝网  添加日期:2018-10-19 13:29:46 浏览: 评论:0 

一. 场景分析

业务服务压力比较大,想把一些占用资源的功能异步到远程处理,比如记录业务日志,文件加密,文件分发到其他文件服务器节点上,检查文件服务器是否已同步,对用户上传的图片进行剪裁生成多份缩略图,视频转换,静态内容生成,清除缓存等等,这些请求耗时长,占用系统资源大,影响业务正常访问。这些问题会经常遇到的,如果这些任务都在用户请求过程中完成,服务器撑不撑得住暂不考虑,单凭用户体验角度考虑来说,那是难以忍受的。

二. 解决之道

对于这种需求,我们可以通过分布式计算,对任务进行拆分,转移到多台服务器上进行异步或同步处理。分布式消息队列有多种实现方式如rabbitmq、gearman等。 在这里主要说gearman—分布式处理系统。gearman由三部分组成:Job server,worker server,client server。 任务服务器job server运行这gearmand进程,来负责处理应用的远程调用请求,并且维护计算任务;工作服务器worker server负责向job server注册函数等待领取任务并执行实际的计算,然后将结果返回给job server;客户端client server提供gearman client API给应用程序调用,API支持多种语言如C、php、perl、python、mysql udf、java、ruby、go等等,主要是向job server添加任务,

流程图如下:

web架构

客户端client server向gearman添加任务时,有三种模式:

1. 同步顺序处理 相当于消息队列,先进先出。

2. 同步并行处理 这种模式会阻塞后面的运行,将互不依赖的任务并行处理,大大缩短整体处理时间。

3. 异步后台处理 将耗时任务交给后台处理,不阻塞当前进程。

三. 应用实例

需要安装的模块有Mail::SendEasy、Gearman::Worker、Gearman::Client、JSON。

# perl -MCPAN -e 'install Mail::SendEasy Gearman::Worker Gearman::Client JSON'

或通过cpanm来安装

在使用异步处理之前,当我们要发送邮件时,会直接这么写,代码如下:

  1. use strict; 
  2.  
  3. use Mail::SendEasy ; 
  4.  
  5. my $mail = new Mail::SendEasy( 
  6.  
  7.     smtp => 'smtp.ttlsa.com' , 
  8.  
  9.     user => 'service@ttlsa.com' , 
  10.  
  11.     pass => 'QQ_qun:39514058'
  12.  
  13. ); 
  14.   
  15. my $status = $mail->send( 
  16.  
  17.     from    => 'service@ttlsa.com' , 
  18.  
  19.     from_title => $mail_title , 
  20.  
  21.     to      => $mail_to , 
  22.  
  23.     subject => $mail_subject , 
  24.  
  25.     msg     => $mail_msg , 
  26.  
  27.     html    => "<b>$mail_msg</b>" , 
  28.  
  29. ); 
  30.  
  31. if (!$status) { print $mail->error ;} 

这存在一个问题, 会长期阻塞在send函数,而无限制等待下去, 直到超时,很可能会拖垮服务器的。我们可以使用gearman,来改变这种发送邮件的方式。第一步,创建一个worker实例SENDMAIL,并向job server注册,等待接收任务并执行发送邮件的操作。第二步,客户端只需要将发送邮件的任务丢给job server便退出,没你什么事了。上代码:

  1. # vim work_SENDMAIL.pl 
  2.  
  3.   
  4.  
  5. use strict; 
  6.  
  7. use Mail::SendEasy ; 
  8.  
  9. use Gearman::Worker; 
  10.  
  11. use JSON; 
  12.  
  13. use Data::Dumper; 
  14.  
  15.   
  16.  
  17. my $gearman_server='127.0.0.1:4730'
  18.  
  19.   
  20.  
  21. my $worker=new Gearman::Worker; 
  22.  
  23. $worker->job_servers($gearman_server); 
  24.  
  25. $worker->register_function(SENDMAIL=>\&sendmail); 
  26.  
  27. $worker->work while 1; 
  28.  
  29.   
  30.  
  31. sub sendmail{ 
  32.  
  33.     my $job=shift; 
  34.  
  35.     my $param=$job->arg; 
  36.  
  37.     my $json = JSON->new->allow_nonref; 
  38.  
  39.     my $hash_ref=$json->decode($param); 
  40.  
  41.     my $addr=$hash_ref->{'email'}; 
  42.  
  43.     my $subject=$hash_ref->{'subject'}; 
  44.  
  45.     my $msg=$hash_ref->{'msg'}; 
  46.  
  47.   
  48.  
  49.     my $mail = new Mail::SendEasy( 
  50.  
  51.         smtp => 'smtp.ttlsa.com' , 
  52.  
  53.         user => 'service@ttlsa.com' , 
  54.  
  55.         pass => 'QQ_qun:39514058'
  56.  
  57.     ); 
  58.  
  59.   
  60.  
  61.     my $status = $mail->send( 
  62.  
  63.         from    => 'service@ttlsa.com' , 
  64.  
  65.         from_title => $subject , 
  66.  
  67.         to      => $addr , 
  68.  
  69.         subject => $subject , 
  70.  
  71.         msg     => $msg , 
  72.  
  73.         html    => "<b>$msg</b>" , 
  74.  
  75.     ); 
  76.  
  77.     if (!$status) { print $mail->error ;} 
  78.  
  79.  
  80.  
  81.  
  82. # vim gearman_client.pl 
  83.  
  84. use strict; 
  85.  
  86. use Gearman::Client; 
  87.  
  88. use JSON; 
  89.  
  90.   
  91.  
  92. my $gearman_server="127.0.0.1:4730"
  93.  
  94. my $worker='SENDMAIL'
  95.  
  96.   
  97.  
  98. my $data={}; 
  99.  
  100. $data->{'subject'} = "www.ttlsa.com --- Operation & Maintenance of Time To Live"
  101.  
  102. $data->{'msg'} = "Date: gettime()  
  103.  
  104. $data->{'email'} = '710117291@qq.com'
  105.  
  106. my $json = JSON->new->allow_nonref; 
  107.  
  108. my $param=$json->encode($data); 
  109.  
  110. gearman_add_job($gearman_server,$worker,$param,2); 
  111.  
  112.   
  113.  
  114. sub gearman_add_job { 
  115.  
  116.     my $server=shift; 
  117.  
  118.     my $function_name=shift; 
  119.  
  120.     my $function_param=shift; 
  121.  
  122.     my $level=shift or 1; 
  123.  
  124.   
  125.  
  126.     my $gearman_client = Gearman::Client->new
  127.  
  128.     $gearman_client->job_servers($server); 
  129.  
  130.     if($level == 1){ 
  131.  
  132.         my $result = $gearman_client->do_task($function_name,\$function_param,{}); 
  133.  
  134.     } 
  135.  
  136.     elsif($level == 2){ 
  137.  
  138.         my $result = $gearman_client->dispatch_background($function_name, \$function_param,{}); 
  139.  
  140.     } 
  141.  
  142.  
  143.   
  144.  
  145. sub gettime { 
  146.  
  147.     my @time=(localtime)[5,4,3,2,1,0]; 
  148.  
  149.     $time[0]+=1900; 
  150.  
  151.     $time[1]+=1; 
  152.  
  153.     return sprintf("%04u-%02u-%02u %02u:%02u:%02u",@time); 
  154.  

完成上面的改造不要认为解决了发送邮件会长期阻塞在send函数,而无限制等待下去的问题了。这其实只是解决了一部分,还有一个问题需要考虑进去,既然采用了异步方式,那么应用程序是不知道邮件是否发送成功的,因此需要记录任务执行的结果,可以将结果写入数据库,定期的对发送失败的邮件进行再次发送,或写个异常处理的worker,捕获发送邮件异常,进行多次尝试发送。

这种方案是我在捕获数据库备份过程中失败时,发邮件报警的应用。

通过这样的异步任务处理,可以很好的解决前面场景提到的问题,完全取决于如何应用gearman了。如有更好的应用可以相互交流。

Tags: 分布式 高性能 实例

分享到: