Gearman是一個分發任務的程序框架,它會對做業進行排隊自動分配到一系列機器上。gearman跨語言跨平臺,很方便的實現異步後臺任務。php官方收錄:http://php.net/manual/zh/book.gearman.php php
如上圖,一個Gearman請求的處理過程涉及三個角色:Client -> Job -> Worker。 c++
Client:請求的發起者,能夠是C,PHP,Perl,MySQL UDF等等。
Job:請求的調度者,用來負責協調把Client發出的請求轉發給合適的Work。
Worker:請求的處理者,能夠是C,PHP,Perl等等。 apache
官方下載,請到https://launchpad.net/gearmand。 centos
yum install boost-devel* gperf* libevent-devel* libuuid-devel
wget https://launchpad.net/gearmand/1.2/1.1.12/+download/gearmand-1.1.12.tar.gz
tar zxvf gearmand…
cd gearmand …
./configure
make && make install 服務器
安裝該擴展須要先安裝一些依賴,建議直接默認./configure,不要指定路徑等。 框架
常見問題1:提示找不到boost>=1.39,明明已經安裝了,這裏應該是沒有安裝gcc-c++,有的機器有gcc卻不必定帶有gcc-c++。yum install gcc-c++應該就能夠了。 異步
常見問題2:安裝完成後啓動不成功,gearmand -d或者gearmand -d -u root都啓動不起來。gearmand -vvv調試模式卻提示未定義選項-v。這時應該是觸發gearmand新版本的bug了,查看log應該能看到「000000 [ main ] socket()(Address family not supported by protocol) -> libgearman-server/gearmand.cc:470」這個錯誤,解決辦法是啓動時添加參數-L 0.0.0.0,限定只綁定ipv4地址,忽略ipv6。或者安裝不高於1.0.2的版本。參見官方反饋帖子:https://bugs.launchpad.net/gearmand/+bug/1134534 socket
參考連接:http://www.usamurai.com/2013/05/01/install-gearman-from-source-in-centos/ 函數
下載擴展程序:http://pecl.php.net/package/gearman ui
wget http://pecl.php.net/get/gearman-1.1.2.tgz
tar zxvf gearman-1….
cd gearman-1 …
phpize
./configure
make && make install
很快就安裝完成,最後會展現so文件的路徑,如: /usr/lib64/php/modules/
在php.ini末尾加上extension=」/usr/lib64/php/modules/gearman.so」,重啓apache,輸出php –info |grep 「gearman」或者php -m或者網頁輸出phpinfo()都能看到已經安裝成功。
常見問題:configure時若是提示找不到php-config,請指定。如–with-php-config=/usr/local/php/bin/php-config,注意要指定完整,不要只寫目錄。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<?php
$client=newGearmanClient();
$client->addServer('127.0.0.1', 4730);//本機能夠直接addServer(),默認服務器端使用4730端口
$client->setCompleteCallback('completeCallBack');//先綁定纔有效
$result1=$client->do('say','do');//do是同步進行,進行處理並返回處理結果。
$result2=$client->doBackground('say','doBackground');//異步進行,只返回處理句柄。
$result3=$client->addTask('say','addTask');//添加任務到隊列,同步進行?經過添加task能夠設置回調函數。
$result4=$client->addTaskBackground('say','addTaskBackground');//添加後臺任務到隊列,異步進行?
$client->runTasks();//運行隊列中的任務,只是do系列不須要runTask()。
echo'result1:';
var_dump($result1);
echo'<br/>';
echo'result2:';
var_dump($result2);
echo'<br/>';
echo'result3:';
var_dump($result3);
echo'<br/>';
echo'result4:';
var_dump($result4);
echo'<br/>';
//綁定回調函數,只對addTask有效
functioncompleteCallBack($task)
{
echo'CompleteCallback!handle result:'.$task->data().'<br/>';
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?php
$worker=newGearmanWorker();
$worker->addServer();
$worker->addFunction('say',function(GearmanJob$job){
$workload=$job->workload();//接收client傳遞的數據
echo'receive data:'.$workload.PHP_EOL;
returnstrrev($workload);//僅做反轉處理
});
//無際循環運行,gearman內部已有處理,不會出現佔用太高死掉的狀況
while($worker->work()){
if($worker->returnCode() !== GEARMAN_SUCCESS){
echo'error'.PHP_EOL;
}
}
|
以上client輸出:
CompleteCallback!handle result:ksaTdda
result1:string(2) 「od」
result2:string(17) 「H:iZ943bixttyZ:87″
result3:object(GearmanTask)#2 (0) { }
result4:object(GearmanTask)#3 (0) { }
worker輸出:
receive data:do
receive data:doBackground
receive data:addTaskBackground
receive data:addTask
因爲worker要長駐後臺時刻準備着被jobserver調用來處理job,因此worker不能死掉,網上有的解決辦法是經過定時任務進行重啓 worker,這應該是不錯的方案。也有說進行多進程守護,但實際上php比較難實現,經過pcntl擴展是其中一種方案,主進程forck出來的子進程 來啓動運行worker,至關於worker做爲主進程的子進程。主進程監護着子進程,worker死掉及時啓動新的一個。但若是主進程死掉呢?因爲主進 程不進行什麼業務處理,死掉的機率要比子進程worker死掉的機率要小很多吧。
如下示例,主進程啓動5個子進程,也就是開啓5個worker,並監護着它們:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
<?php
declare(ticks = 1);
$num= 5;//最大子進程數
$child= 0;//當前子進程數
//信號處理函數
functionsig_handler($sig)
{
global$child;
switch($sig) {
caseSIGCHLD:
$child--;
echo'SIGCHLD received! now we have '.$child.' process'.PHP_EOL;
break;
caseSIGINT:
$child--;
echo'SIGINT received! now we have '.$child.' process'.PHP_EOL;
break;
caseSIGTERM:
$child--;
echo'SIGTERM received! now we have '.$child.' process'.PHP_EOL;
break;
default:
# code...
break;
}
}
//安裝信號處理器
pcntl_signal(SIGTERM,"sig_handler");//進程被kill時發出的信號
// pcntl_signal(SIGHUP, "sig_handler");//終端關閉時發出的信號
pcntl_signal(SIGINT,"sig_handler");//中斷進程信號,如Ctrl+C
pcntl_signal(SIGCHLD,"sig_handler");//進程退出信號
while(true)
{
$child++;
$parentpid=getmypid();
$pid= pcntl_fork();//一分爲二,父進程和子進程都會執行如下代碼
if($pid== -1)
{
exit("can not fork!");//出錯
}elseif($pid> 0){
//父進程處理代碼
echo'I am parent.my pid is'.$pid.' and my parent pid is'.$parentpid.PHP_EOL;
if($child>=$num)
{
pcntl_wait($status);//掛起,while語句不會繼續執行。等待子進程結束,防止子進程成爲殭屍進程
}
}elseif($pid== 0){
//子進程代碼
echo'I am child, and my parent pid is '.$parentpid." my pid is ".getmypid()." now have $child process".PHP_EOL;
//執行具體代碼
pcntl_exec('/usr/bin/php',array('/var/www/test/mywork.php'));
}
pcntl_signal_dispatch();//分發信號,使安裝的信號處理器能接收。
//低於php5.3該函數無效,但有開頭的declare (ticks = 1);表示每執行一條低級指令,
//就檢查一次信號,若是檢測到註冊的信號,就調用其信號處理器
sleep(rand(3,5));//防止100%佔用
}
|
參考連接:http://huoding.com/2012/10/30/196,http://www.zrwm.com/?cat=80