服務(wù)器的訪問壓力比較大時(shí),我們可以通過負(fù)載均衡來將負(fù)載分散到多臺(tái)服務(wù)器上.但有些比較耗時(shí)的請(qǐng)求.比如:
1. 需要連接郵件服務(wù)器,發(fā)送一封超長的HTML郵件。
2. 需要對(duì)用戶上傳的圖片進(jìn)行裁剪,生成多份縮略圖。
3. 需要將用戶上傳的文件分發(fā)到多臺(tái)服務(wù)器上。
在我們的日常應(yīng)用中經(jīng)常遇到,用戶需要等待一段時(shí)間,這個(gè)請(qǐng)求才能完成,在用戶上傳照片時(shí),照片上傳成功后,然后是裁剪,最后生成縮略圖,在這么一個(gè)過程中,用戶只能等待,所以對(duì)用戶的體驗(yàn)來講是相當(dāng)不好的。可能在用戶第一次使用過后,下次就很難再讓他使用這個(gè)圖片的上傳功能了。
那么有沒有一種方法將這些處理過程放在后臺(tái)慢慢運(yùn)行呢?答案是肯定的,通過分布式處理可以將這些比較耗時(shí)的任務(wù)放在后臺(tái),甚至分散到多臺(tái)服務(wù)器上去處理,對(duì)于這個(gè)問題異步處理就派上了用場。
現(xiàn)在有很多開源的軟件能實(shí)現(xiàn)異步通信,比如ActiveMQ、Hadoop、Gearman和MecacheMQ等,它們巧妙的將計(jì)算轉(zhuǎn)移到其他服務(wù)器上,而這一切都是隱藏在API中,同時(shí),這些機(jī)制都是跨語言的,可以用PHP來分配一個(gè)任務(wù),然后將后臺(tái)的C/C++程序來進(jìn)行處理,這一切都不是什么問題。
下面通過php和Gearman來演示異步的處理過程.
一、Gearman的安裝
tar zxvf gearmand-0.11.tar.gzcd gearmand-0.11./configure --prefix=/usr/local/gearmanmakemake install
二、安裝Gearman PHP extension.
tar zxf gearman-0.6.0.tgzcd gearman-0.6.0 phpize ./configure make
將module目錄上的gearman.so拷貝到php的module目錄下,我機(jī)器的目錄在/usr/lib/php5/20060613+lfs/.
cp module/gearman.so /usr/lib/php5/20060613+lfs/.
然后在php.ini中添加
extension = “gearman.so”
最后重啟apache服務(wù)器.
三、啟動(dòng)Gearman服務(wù)
cd /usr/local/gearman/sbin./gearman -d -u root
Job的默認(rèn)端口為4730,可以通過
[root@serv_1 sbin]# netstat -nl | grep 4730tcp 0 0 :::4730 :::* LISTEN
是否啟動(dòng).
通過bin/gearman這個(gè)工具,我們可以來體驗(yàn)Gearman的功能.
啟動(dòng)Worker:
./gearman -w -f wc -- wc -l &[1] 2547
運(yùn)行 Client:
./gearman -f wc < /etc/passwd38
上面的例子中,Worker中定義了一個(gè)函數(shù) wc,主要功能用來統(tǒng)計(jì)文本的行數(shù),而client將/etc/passwd這個(gè)文件的內(nèi)容傳給Worker來處理,最后得出passwd有38行文本.
四、使用php來編寫Worker和Client.
這里任務(wù)還是用官方的一個(gè)例子來說明,將一個(gè)字符串反轉(zhuǎn)后輸出,為了模擬這個(gè)過程耗時(shí),我們將程序sleep1秒種,并將處理函數(shù)連續(xù)調(diào)用100次.
先來看看一般情況下我們的程序處理方式.
1、不使用異步處理的方式.
for ($i = 0; $i < 100; $i ++) {my_reverse_function("helloword!")} function my_reverse_function($str) {sleep(1);echo strrev($str);echo "\n";}
可以看出這個(gè)處理過程是相當(dāng)耗時(shí)的,整個(gè)程序處理完成基本需要100秒.
2、使用基于Gearman的php異步處理方式
編寫Worker.php
$worker = new GearmanWorker();$worker->addServer();//$worker->addServer();$worker->addFunction("reverse","my_reverse_function"); while ($worker->work()); function my_reverse_function($job) {sleep(1);echo strrev($job->workload());echo "\n";}
啟動(dòng)Worker.php.
php Worker.php
這時(shí)Worker.php作為一個(gè)后臺(tái)服務(wù),處于一個(gè)監(jiān)聽狀態(tài),只要有任務(wù)過來,馬上進(jìn)行處理.
編寫Client.php
$client = new GearmanClient();$client->addServer();echo "beginning...\n";for ($i = 0; $i < 100; $i++) {$client->doBackground('reverse',($i+1).'_Hello World!')."\n";}echo "finishing...\n";
這里的Client.php就是任務(wù)的發(fā)起者,這里將連續(xù)處理100次字符串反轉(zhuǎn)任務(wù).
運(yùn)行Client.php
$ php client.phpbeginning...finishing...
這時(shí)Client.php將任務(wù)交給了Worker.php來處理,但它本身很快就運(yùn)行完成,這里再回過來來看Worker.php的運(yùn)行狀態(tài),如下.
$ php Worker.php!dlroW olleH_1!dlroW olleH_2!dlroW olleH_3!dlroW olleH_4!dlroW olleH_5!dlroW olleH_6!dlroW olleH_7!dlroW olleH_8!dlroW olleH_9!dlroW olleH_01!dlroW olleH_11!dlroW olleH_21!dlroW olleH_31!dlroW olleH_41!dlroW olleH_51!dlroW olleH_61....
發(fā)現(xiàn)在Client.php運(yùn)行完成后,Worker.php則是每秒輸出一條處理結(jié)果,直到所有的任務(wù)全部處理完成.
以上主要是說明異步處理是怎么回事,在目前的開發(fā)中,怎么將耗時(shí)過長的任務(wù)進(jìn)行異步來處理。但在日常的開發(fā)中,可能有些任務(wù)耗時(shí)確實(shí)很長,但處理后的結(jié)果又需要很快的反饋給用戶時(shí),這里我們就需要將處理后的結(jié)果即時(shí)的保存到數(shù)據(jù)庫中,然后靠專門的程序?qū)⒔Y(jié)果反饋給用戶??赡苓€會(huì)有一個(gè)問題,這樣異步處理,萬一處理失敗了怎么辦,同樣的道理,我們可以將每個(gè)任務(wù)的處理狀態(tài)保存起來,然后用程序定時(shí)的去掃描,將處理失敗的任務(wù)再重啟執(zhí)行一次,反復(fù)下去,直接它成功完成為止。
參考資料.
http://baike.baidu.com/view/2926980.htm?fr=ala0_1_1
http://cn.php.net/gearman
http://gearman.org