最近在作一个服务器集群管理的web项目,须要处理一些极其耗时的操做,好比磁盘格式化分区。对于这个需求,最开始的想法是,为了让节点上的rpc(远程过程调用) service端尽量简单(简单到只须要popen执行一条指令便可,有时间我再专门写一篇博客讲讲这个项目的rpc是如何实现的),咱们选择了让web端直接等待处理结果,那么问题来了,如何保证用户没必要等待,又能保证任务准确的执行呢?php
简单的rpc结构以下图linux
以往在处理一些稍微耗时的操做,能够经过优化代码结构,优化数据库操做次数,起一些线程来处理一些简单的好比发邮件,生成大的压缩文件,提取视频缩量图,服务器间互访等等操做,来避免用户在web页面的等待。laravel
但如今这个操做显然不能用以前的这些方法作,由于如今的操做哪怕只执行一次,都是很是耗时的,更况且可能须要处理的多是上百上千台服务器。这是在线程层面很难作,要知道在响应请求的web进程中起一个线程来作的话,在响应完断开tcp链接以后,这个进程极可能被kill掉,像Apache就是这样,固然能够经过配置改变apache的行为,但显然不太靠谱。git
更好的作法是在web服务器上起一个守护进程去作这个事情,那么问题就在于如何建立守护进程了,好在laravel帮咱们考虑了这个事情。github
laravel的队列默认是以sync(同步)的方式来处理多个任务,这显然不是咱们想要的。鉴于这个项目使用的是laravel4.1版本,我选择了beanstalkd来实现异步处理多个任务。web
其中beanstalkd是一种比较专业的队列服务驱动器,是一个常驻后台服务,咱们能够经过它提供的接口来把任务提交给它,由它建立的守护进程来执行队列。shell
我开发的电脑为CentOS5.4,版本比较低,因此装的过程当中仍是遇到些麻烦数据库
首先执行下面的指令apache
1 wget ftp://fr2.rpmfind.net/linux/epel/5/ppc/epel-release-5-4.noarch.rpm 2 rpm -ivh epel-release-5-4.noarch.rpm 3 yum makecache 4 yum search beanstalkd
但最后发现找不到这个软件,因而将yum的源换成了163的json
1 cd /etc/yum.repos.d 2 wget http://mirrors.163.com/.help/CentOS5-Base-163.repo 3 mv CentOS-Base.repo CentOS-Base.repo.bak
再次makecache && install 就OK了,安装完后启动beanstalkd服务
1 service beanstalkd start
另外能够搜索到beanstalkd的配置文件放在了sysconfig下
beanstalkd的php驱动包为pda/pheanstalk
进入laravel的protected目录,composer.json在这个目录下
执行
1 composer require pda/pheanstalk 2.*
出现以下的错误
,
能够看出镜像地址响应 502,因此须要给composer找一个可用的镜像 http://www.phpcomposer.com/
修改~/.composer/config.json以下
而后回到protected目录,再执行前面安装驱动的命令安装,这回出现了不同的错误
上面的php包中,第一个是为了phpstorm的对laravel更好的支持,后面一个symfony/yaml已经安装,并不须要升级,因此修改composer.json,直接将这两个项目删除掉就好了
删除完以后,再次执行安装命令安装
能够看到终于成功了,能够经过 composer show -i 查看安装了哪些包
在TestController中添加一个action
1 class TestController extends BaseController 2 { 3 4 public function getQueue(){ 5 // 6 Log::info("添加一个对列任务"); 7 Queue::push('SendEmail',array('message'=>'哈哈')); 8 Log::info('任务添加完毕'); 9 exit; 10 } 11 12 }
在app目录下新建tasks目录,并修改protected/composer.json和app/global.php,将这个目录加到类加载路径中
修改global.php
1 ClassLoader::addDirectories(array( 2 3 app_path().'/commands', 4 app_path().'/controllers', 5 app_path().'/models', 6 app_path().'/database/seeds', 7 app_path().'/library', 8 app_path().'/tasks', 9 ));
修改composer.json
1 "classmap": [ 2 "app/commands", 3 "app/controllers", 4 "app/models", 5 "app/database/migrations", 6 "app/database/seeds", 7 "app/tasks", 8 "app/tests/TestCase.php" 9 ]
之后的耗时调度任务的代码就放在这个目录下面了
首先新建一个BaseTask.php
1 /** 2 * Created by PhpStorm. 3 * User: Administrator 4 * Date: 2015/8/19 0019 5 * Time: 11:55 6 */ 7 abstract class BaseTask 8 { 9 public abstract function fire($job,$data); 10 }
而后新建一个SendMail.php
1 /** 2 * Created by PhpStorm. 3 * User: Administrator 4 * Date: 2015/8/19 0019 5 * Time: 11:50 6 */ 7 class SendEmail extends BaseTask 8 { 9 10 public function fire($job, $data) 11 { 12 // TODO: Implement fire() method. 13 Log::info("对列任务执行".json_encode($data)."Time : ".time()); 14 sleep(30); 15 Log::info("对列任务执行完毕".time()); 16 // 将任务从队列冲删除 17 $job->delete(); 18 // 将任务返回到队列 19 20 // $job->release(); 21 22 } 23 }
1 'default' => 'beanstalkd',
1 'beanstalkd' => array( 2 'driver' => 'beanstalkd', 3 'host' => 'localhost', 4 'queue' => 'default', 5 'ttr' => 60, 6 ),
1 php artisan queue:work 2 php artisan queue:listen
而后访问url,http://192.168.1.10/ssanlv/test/queue,能够发现请求立刻就完成了,页面并无等待
查看protected/app/storage/logs/laravel.log 能够看到下面的内容
508-478 恰好30秒
下面测试一个实际的问题,印象中apache服务器与客户端在请求完成断开链接后会kill掉负责处理的httpd进程,只有配置了keep-alive参数在会将进程保留到apache进程池中,因此,但用户请求一个耗时操做以后,关闭了浏览器,这个处理耗时任务的守护进程会不会也被kill掉呢?固然,其实有点多虑了,当响应完成以后tcp连接已经被断开掉了,若是进程会被kill掉,那么早就kill掉了,跟你浏览器关没关应该没多大关系,仍是试试吧,实践才是硬道理
这里将SendMial中的sleep时间改长一点,改成 600秒
最后发现没有执行完,能够看到listen报出异常
很显然执行超时,看来是前面设置的ttr的问题
将ttr注释掉或者修改掉更高的值,发现仍是不行,最后在仔细看看报错信息,发现
因此改变命令的执行方式
1 php artisan queue:listen --timeout=800
最后命令任务成功执行完毕
能够看到 1353-753 = 600 刚恰好
另外,看样子 这个任务对列应该是被保存起来了,当我没有启动 listen时,任务怎么都不会处理,但我一但启动listen,前面添加的任务就会立马执行
但最后仍是有个问题这个是对列形式进行处理,要启动下一个对列任务,必须等上一个对列任务执行完毕,不过以前曾看到过,一个work对应一个任务队列,那么我彻底能够起多个任务队列,有点多核CPU的调度哦。
最后,再跟一位大神讨论了一下,探讨出了另一个更加优秀的办法,虽然会加剧节点上rpc service代码的复杂度,不过也不是很麻烦。
这种方式就是回调,管理集群的web服务器能够不用等待,只需以下步骤,
很显然,这种方式更加可靠,也大大减轻了web 服务器的负担,要知道Linux 系统的线程数是有限制的,但这要耗时任务多了,若是然服务器去等,无论啥策略都极可能吧服务器整垮。