laravel 任务队列_laravel队列-让守护进程处理耗时任务
待解決的問(wèn)題
最近在做一個(gè)服務(wù)器集群管理的web項(xiàng)目,需要處理一些極其耗時(shí)的操作,比如磁盤格式化分區(qū)。對(duì)于這個(gè)需求,最開(kāi)始的想法是,為了讓節(jié)點(diǎn)上的rpc(遠(yuǎn)程過(guò)程調(diào)用)?service端盡可能簡(jiǎn)單(簡(jiǎn)單到只需要popen執(zhí)行一條指令即可,有時(shí)間我再專門寫(xiě)一篇博客講講這個(gè)項(xiàng)目的rpc是如何實(shí)現(xiàn)的),我們選擇了讓web端直接等待處理結(jié)果,那么問(wèn)題來(lái)了,如何保證用戶不必等待,又能保證任務(wù)準(zhǔn)確的執(zhí)行呢?
簡(jiǎn)單的rpc結(jié)構(gòu)如下圖
以往在處理一些稍微耗時(shí)的操作,可以通過(guò)優(yōu)化代碼結(jié)構(gòu),優(yōu)化數(shù)據(jù)庫(kù)操作次數(shù),起一些線程來(lái)處理一些簡(jiǎn)單的比如發(fā)郵件,生成大的壓縮文件,提取視頻縮量圖,服務(wù)器間互訪等等操作,來(lái)避免用戶在web頁(yè)面的等待。
但現(xiàn)在這個(gè)操作顯然不能用之前的這些方法做,因?yàn)楝F(xiàn)在的操作哪怕只執(zhí)行一次,都是非常耗時(shí)的,更何況可能需要處理的可能是上百上千臺(tái)服務(wù)器。這是在線程層面很難做,要知道在響應(yīng)請(qǐng)求的web進(jìn)程中起一個(gè)線程來(lái)做的話,在響應(yīng)完斷開(kāi)tcp連接之后,這個(gè)進(jìn)程很可能被kill掉,像Apache就是這樣,當(dāng)然可以通過(guò)配置改變apache的行為,但顯然不太靠譜。
更好的做法是在web服務(wù)器上起一個(gè)守護(hù)進(jìn)程去做這個(gè)事情,那么問(wèn)題就在于如何創(chuàng)建守護(hù)進(jìn)程了,好在laravel幫我們考慮了這個(gè)事情。
Laravel的隊(duì)列
laravel的隊(duì)列默認(rèn)是以sync(同步)的方式來(lái)處理多個(gè)任務(wù),這顯然不是我們想要的。鑒于這個(gè)項(xiàng)目使用的是laravel4.1版本,我選擇了beanstalkd來(lái)實(shí)現(xiàn)異步處理多個(gè)任務(wù)。
其中beanstalkd是一種比較專業(yè)的隊(duì)列服務(wù)驅(qū)動(dòng)器,是一個(gè)常駐后臺(tái)服務(wù),我們可以通過(guò)它提供的接口來(lái)把任務(wù)提交給它,由它創(chuàng)建的守護(hù)進(jìn)程來(lái)執(zhí)行隊(duì)列。
配置隊(duì)列執(zhí)行環(huán)境
1.安裝beanstalkd服務(wù)
我開(kāi)發(fā)的電腦為CentOS5.4,版本比較低,所以裝的過(guò)程中還是遇到些麻煩
首先執(zhí)行下面的指令
1 wget ftp://fr2.rpmfind.net/linux/epel/5/ppc/epel-release-5-4.noarch.rpm
2 rpm -ivh epel-release-5-4.noarch.rpm3 yummakecache4 yum search beanstalkd
但最后發(fā)現(xiàn)找不到這個(gè)軟件,于是將yum的源換成了163的
1 cd /etc/yum.repos.d2 wget http://mirrors.163.com/.help/CentOS5-Base-163.repo
3 mv CentOS-Base.repo CentOS-Base.repo.bak
再次makecache && install 就OK了,安裝完后啟動(dòng)beanstalkd服務(wù)
1 service beanstalkd start
另外可以搜索到beanstalkd的配置文件放在了sysconfig下
為laravel添加beanstalkd的驅(qū)動(dòng)
beanstalkd的php驅(qū)動(dòng)包為pda/pheanstalk
進(jìn)入laravel的protected目錄,composer.json在這個(gè)目錄下
執(zhí)行
1 composer require pda/pheanstalk 2.*
出現(xiàn)如下的錯(cuò)誤
,
可以看出鏡像地址響應(yīng) 502,所以需要給composer找一個(gè)可用的鏡像?http://www.phpcomposer.com/
修改~/.composer/config.json如下
然后回到protected目錄,再執(zhí)行前面安裝驅(qū)動(dòng)的命令安裝,這回出現(xiàn)了不一樣的錯(cuò)誤
上面的php包中,第一個(gè)是為了phpstorm的對(duì)laravel更好的支持,后面一個(gè)symfony/yaml已經(jīng)安裝,并不需要升級(jí),所以修改composer.json,直接將這兩個(gè)項(xiàng)目刪除掉就行了
刪除完之后,再次執(zhí)行安裝命令安裝
可以看到終于成功了,可以通過(guò) composer show -i 查看安裝了哪些包
測(cè)試
在TestController中添加一個(gè)action
1 class TestController extendsBaseController2 {3
4 public functiongetQueue(){5 //6 Log::info("添加一個(gè)對(duì)列任務(wù)");7 Queue::push('SendEmail',array('message'=>'哈哈'));8 Log::info('任務(wù)添加完畢');9 exit;10 }11
12 }
在app目錄下新建tasks目錄,并修改protected/composer.json和app/global.php,將這個(gè)目錄加到類加載路徑中
修改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 ]
以后的耗時(shí)調(diào)度任務(wù)的代碼就放在這個(gè)目錄下面了
首先新建一個(gè)BaseTask.php
1 /**2 * Created by PhpStorm.3 * User: Administrator4 * Date: 2015/8/19 00195 * Time: 11:556 */
7 abstract classBaseTask8 {9 public abstract function fire($job,$data);10 }
然后新建一個(gè)SendMail.php
1 /**2 * Created by PhpStorm.3 * User: Administrator4 * Date: 2015/8/19 00195 * Time: 11:506 */
7 class SendEmail extendsBaseTask8 {9
10 public function fire($job, $data)11 {12 //TODO: Implement fire() method.
13 Log::info("對(duì)列任務(wù)執(zhí)行".json_encode($data)."Time : ".time());14 sleep(30);15 Log::info("對(duì)列任務(wù)執(zhí)行完畢".time());16 //將任務(wù)從隊(duì)列沖刪除
17 $job->delete();18 //將任務(wù)返回到隊(duì)列19
20 // $job->release();
21
22 }23 }
最后就是去修改config目錄下的配置文件queue.php文件了,修改為
1 'default' => 'beanstalkd',
1 'beanstalkd' => array(2 'driver' => 'beanstalkd',
3 'host' => 'localhost',
4 'queue' => 'default',
5 'ttr' => 60,
6 ),
關(guān)于ttr的解釋這里有一個(gè)
表示time to run ,這個(gè)參數(shù)可以覆蓋默認(rèn)參數(shù)讓Beanstalkd?檢測(cè)是否在這個(gè)時(shí)間內(nèi)完成
至此配置和寫(xiě)代碼完成,在shell中執(zhí)行,要在protected目錄下,artisan文件在這個(gè)目錄下
1 php artisan queue:work2 php artisan queue:listen
查看protected/app/storage/logs/laravel.log 可以看到下面的內(nèi)容
508-478 剛好30秒
下面測(cè)試一個(gè)實(shí)際的問(wèn)題,印象中apache服務(wù)器與客戶端在請(qǐng)求完成斷開(kāi)連接后會(huì)kill掉負(fù)責(zé)處理的httpd進(jìn)程,只有配置了keep-alive參數(shù)在會(huì)將進(jìn)程保留到apache進(jìn)程池中,所以,但用戶請(qǐng)求一個(gè)耗時(shí)操作之后,關(guān)閉了瀏覽器,這個(gè)處理耗時(shí)任務(wù)的守護(hù)進(jìn)程會(huì)不會(huì)也被kill掉呢?當(dāng)然,其實(shí)有點(diǎn)多慮了,當(dāng)響應(yīng)完成之后tcp鏈接已經(jīng)被斷開(kāi)掉了,如果進(jìn)程會(huì)被kill掉,那么早就kill掉了,跟你瀏覽器關(guān)沒(méi)關(guān)應(yīng)該沒(méi)多大關(guān)系,還是試試吧,實(shí)踐才是硬道理
這里將SendMial中的sleep時(shí)間改長(zhǎng)一點(diǎn),改為 600秒
最后發(fā)現(xiàn)沒(méi)有執(zhí)行完,可以看到listen報(bào)出異常
很顯然執(zhí)行超時(shí),看來(lái)是前面設(shè)置的ttr的問(wèn)題
將ttr注釋掉或者修改掉更高的值,發(fā)現(xiàn)還是不行,最后在仔細(xì)看看報(bào)錯(cuò)信息,發(fā)現(xiàn)
所以改變命令的執(zhí)行方式
1 php artisan queue:listen --timeout=800
最后命令任務(wù)成功執(zhí)行完畢
可以看到 1353-753 = 600 剛剛好
另外,看樣子 這個(gè)任務(wù)對(duì)列應(yīng)該是被保存起來(lái)了,當(dāng)我沒(méi)有啟動(dòng) listen時(shí),任務(wù)怎么都不會(huì)處理,但我一但啟動(dòng)listen,前面添加的任務(wù)就會(huì)立馬執(zhí)行
但最后還是有個(gè)問(wèn)題這個(gè)是對(duì)列形式進(jìn)行處理,要啟動(dòng)下一個(gè)對(duì)列任務(wù),必須等上一個(gè)對(duì)列任務(wù)執(zhí)行完畢,不過(guò)之前曾看到過(guò),一個(gè)work對(duì)應(yīng)一個(gè)任務(wù)隊(duì)列,那么我完全可以起多個(gè)任務(wù)隊(duì)列,有點(diǎn)多核CPU的調(diào)度哦。
更好的辦法
最后,再跟一位大神討論了一下,探討出了另外一個(gè)更加優(yōu)秀的辦法,雖然會(huì)加重節(jié)點(diǎn)上rpc service代碼的復(fù)雜度,不過(guò)也不是很麻煩。
這種方式就是回調(diào),管理集群的web服務(wù)器可以不用等待,只需如下步驟,
通過(guò)web服務(wù)器上的rpc client向要執(zhí)行耗時(shí)操作的節(jié)點(diǎn)上的rpc service發(fā)送一條指令,
節(jié)點(diǎn)上的rpc service收到指令后,不先執(zhí)行指令,而是馬上向web服務(wù)器,也就是rpc client返回一個(gè)任務(wù)ID。
web服務(wù)器將這個(gè)id作為一條任務(wù)記錄保存到數(shù)據(jù)庫(kù)。
節(jié)點(diǎn)上的rpc service處理指令,至于處理指令,也是在節(jié)點(diǎn)上在單獨(dú)起一個(gè)進(jìn)程 P 來(lái)處理,因?yàn)閞pc service也不能讓rpc client傻等著
處理進(jìn)程 P 處理完了之后,將執(zhí)行結(jié)果和任務(wù)ID作為參數(shù),回調(diào)web服務(wù)器的一個(gè)web接口
web服務(wù)器接到rpc service的回調(diào)之后,通過(guò)ID查找到任務(wù),更新任務(wù)的執(zhí)行狀態(tài),更新數(shù)據(jù)
很顯然,這種方式更加可靠,也大大減輕了web 服務(wù)器的負(fù)擔(dān),要知道Linux 系統(tǒng)的線程數(shù)是有限制的,但這要耗時(shí)任務(wù)多了,如果然服務(wù)器去等,不管啥策略都很可能吧服務(wù)器整垮。
總結(jié)
以上是生活随笔為你收集整理的laravel 任务队列_laravel队列-让守护进程处理耗时任务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python相同怎么写_这两个index
- 下一篇: saas- -m ihrm 项目_Con