Gearman是一個分發任務的程序框架,它會對作業進行排隊自動分配到一系列機器上。gearman跨語言跨平台,很方便的實現異步后台任務。php官方收錄:http://php.net/manual/zh/book.gearman.php
二、安裝
1、安裝服務器端:
官方下載,請到https://launchpad.net/gearmand。
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
參考鏈接:http://www.usamurai.com/2013/05/01/install-gearman-from-source-in-centos/
2、安裝gearman的php擴展
下載擴展程序:http://pecl.php.net/package/gearman
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,注意要指定完整,不要只寫目錄。
三、實例
client:
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/>';
}
|
worker:
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
四、pcntl擴展實現粗略的多worker守護
由於worker要長駐后台時刻准備着被jobserver調用來處理job,所以worker不能死掉,網上有的解決辦法是通過定時任務進行重啟 worker,這應該是不錯的方案。也有說進行多進程守護,但實際上php比較難實現,通過pcntl擴展是其中一種方案,主進程forck出來的子進程 來啟動運行worker,相當於worker作為主進程的子進程。主進程監護着子進程,worker死掉及時啟動新的一個。但如果主進程死掉呢?由於主進 程不進行什么業務處理,死掉的概率要比子進程worker死掉的概率要小不少吧。
五、linux直接運行 (這樣可以防止php端運行時php掛掉無法執行)
可以現在我們的php中先寫一個類:比如wxPushMsg.class 然后我們去調用它
/*get data by gearman*/
$data = file_get_contents('php://stdin');//接收gearman 傳遞過來的data
$data = json_decode($data, true);
$send_data = $data['data'];
$send_tmp = $data['tmp_id'];
$send_test = $data['test'];
WxPubMsg::send_batch($send_data, $send_tmp, $send_test);
控制器端調用gearman Task
$client=newGearmanClient();
$client->addServer();//本機可以直接addServer(),默認服務器端使用4730端口
$client->addTask('wx_push',json_encode($push_data));//
$client->runTasks();//運行隊列中的任務,只是do系列不需要runTask()。
然后我們linux運行wxPushMsg ;
#!/bin/bash
#this is for gearman worker
#@author FLY
#2016-11-08 PM
# ./gmworker.sh gmname xxx.php cmd(status,start,stop,retart)
if [ "$#" -ge 3 ]
then
echo "ready to go"
echo "name:"$1
echo "file:"$2
echo "cmd:"$3 #start ,stop,restart
else
echo "err:need 3 arguments!"
exit
fi
gname=$1
gflie=$2
cmd=$3
#start
function stratr_worker(){
name=$1
file=$2
script=$(cd `dirname $0`; pwd)'/'$file
echo "path:"$script
echo "cmd:gearman -w -f "$name" -N $script >/dev/null 2>&1 &"
eval "gearman -w -f "$name" -N $script >/dev/null 2>&1 &"
}
#kill pro
function stop_worker(){
name=$1
file=$2
declare -a list=($(ps -ef|grep "$name"|awk '{print $2}'))
for pid in "${list[@]}"
do
eval "kill -9 $pid"
# echo "kill -9 $pid"
done
#eval "for pid in ; do kill -9 $pid; done"
}
#get status
function status_worker(){
name=$1
declare -a list=($(ps -ef|grep "$name"|awk '{print $2 }'))
for worker in "${list[@]}"
do
echo "-----------------------------------------"
echo "pid:$worker `cat /proc/$worker/cmdline`"
done
}
#cmd exec
case $cmd in
"start")
stratr_worker $gname $gflie
;;
"stop")
stop_worker $gname $gfile
;;
"restart")
stop_worker $gname $gfile
stratr_worker $gname $gfile
;;
"status")
status_worker $gname $gfile
;;
*)
echo "cmd [$cmd] wrong, do nothing!"
;;
esac
跨服務器使用:
gearman -h 服務器ip -w -f task_name -N gm_worker_file_path
2、如果出現"[php_gearman.lo] Error 1"錯誤, 可以將"php_gearman.loT" 拷貝一個 "php_gearman.lo",然后再make
3、如果啟動php的時候gearman.so無效,則gearman擴展版本要到1.1.0版本才行