淺談PHP進(jìn)程管理
這篇文章是對(duì)之前一篇文章的補(bǔ)充和改進(jìn), 創(chuàng)建一個(gè)主(master)進(jìn)程,主進(jìn)程安裝定時(shí)器,每隔5分鐘檢測(cè)一次隊(duì)列長(zhǎng)度,根據(jù)隊(duì)列長(zhǎng)度計(jì)算需要的worker進(jìn)程,
然后創(chuàng)建或者殺掉子進(jìn)程。這樣做的好處是防止隊(duì)列堆積,任務(wù)得不到及時(shí)處理。更新業(yè)務(wù)代碼,只需要reload操作即可。
整個(gè)流程有以下知識(shí)點(diǎn):
創(chuàng)建守護(hù)進(jìn)程的步驟:
- 設(shè)置默認(rèn)文件權(quán)限
- fork一個(gè)進(jìn)程,父進(jìn)程退出
- 調(diào)用setsid創(chuàng)建一個(gè)新的會(huì)話
- 將當(dāng)前工作目錄更改為根目錄
- 關(guān)閉不再需要的文件描述符
使用信號(hào)實(shí)現(xiàn)定時(shí)器
上一篇定時(shí)器依賴于系統(tǒng)的定時(shí)任務(wù),這次使用鬧鐘信號(hào)實(shí)現(xiàn),php 5.3.0以下的版本依賴于ticks,5.3.0及以上版本可使用pcntl_signal_dispatch
信號(hào):提供了一種異步事件處理的方法,在某個(gè)信號(hào)出現(xiàn)時(shí),進(jìn)程有以下三種方式對(duì)信號(hào)進(jìn)行處理
- 忽略此信號(hào)
- 捕捉信號(hào)
- 執(zhí)行系統(tǒng)默認(rèn)動(dòng)作,大多數(shù)信號(hào)的默認(rèn)動(dòng)作是終止該進(jìn)程
常見(jiàn)信號(hào)
SIGKILL,SIGSTOP是兩種不能被用戶忽略和捕捉的信號(hào)
SIGINT(2):程序終止信號(hào),通常是Ctrl-C)時(shí)發(fā)出,用于通知前臺(tái)進(jìn)程組終止進(jìn)程
SIGQUIT(3):和SIGINT類(lèi)似, 但由QUIT字符(通常是Ctrl+/)來(lái)控制. 進(jìn)程收到該消息退出時(shí)會(huì)產(chǎn)生core文件
SIGKILL(9):立即終止進(jìn)程,不可被忽略捕捉或阻塞
SIGUSR1(10):用戶定義信號(hào)
SIGUSR2(12):留給用戶使用
SIGALRM(14):鬧鐘信號(hào)
SIGTERM(15):終止進(jìn)程,可被程序捕捉,使得進(jìn)程可以執(zhí)行完清理操作。
SIGSTOP(19):停止一個(gè)進(jìn)程,該進(jìn)程還未結(jié)束, 只是暫停執(zhí)行
防止產(chǎn)生僵尸進(jìn)程
所有的進(jìn)程在退出的時(shí)候都會(huì)成為僵尸進(jìn)程,這時(shí)候如果父進(jìn)程還在運(yùn)行,沒(méi)有調(diào)用wait或者waitpid,則僵尸進(jìn)程占用的資源不會(huì)被清理,如果父進(jìn)程已終止,僵尸進(jìn)程由init進(jìn)程進(jìn)行清理。
抽調(diào)業(yè)務(wù)代碼,主要代碼如下
其中要注意的一點(diǎn),創(chuàng)建守護(hù)進(jìn)程關(guān)閉輸入輸出,錯(cuò)誤輸出流的時(shí)候,如果代碼后面有echo等輸出字符,將出現(xiàn)致命錯(cuò)誤,需要在php代碼中重定向輸出流到/dev/null?;蛘咴诮K端啟動(dòng)進(jìn)程的時(shí)候進(jìn)行重定向
<?php define('PROC_MAX', 10); define('PROC_MIN', 5); $cmd = $argv[1]; $aPid = []; $pidFile = __DIR__ . '/pid.pid'; $pid = file_get_contents($pidFile); switch($cmd){ case 'start' : if(posix_kill($pid, 0)){ echo "gamelog process is already exsits!\n"; return false; } //設(shè)置默認(rèn)文件權(quán)限 umask(022); //fork $pid = pcntl_fork(); if($pid < 0){ exit('fork error!'); }else if($pid > 0){ exit; } //脫離當(dāng)前終端 posix_setsid(); //將當(dāng)前工作目錄更改為根目錄 chdir('/'); //關(guān)閉文件描述符 fclose(STDIN); fclose(STDOUT); fclose(STDERR); //重定向輸入輸出 global $STDOUT, $STDERR; $STDOUT = fopen('/dev/null', 'a'); $STDERR = fopen('/dev/null', 'a'); cli_set_process_title('gamelog:master'); $pid = posix_getpid(); file_put_contents($pidFile, $pid); //鬧鐘信號(hào) pcntl_signal(SIGALRM, function() use (&$aPid) { pcntl_alarm(300); $workerNum = mt_rand(1, 20);//此處檢測(cè)你需要的進(jìn)程數(shù) $daemonNum = count($aPid); ($workerNum > PROC_MAX) && ($workerNum = PROC_MAX); if($daemonNum < $workerNum){ $procNum = $workerNum - $daemonNum; $procNum = max(PROC_MIN, $procNum); for($p = 1; $p <= $procNum; $p++){ $pid = pcntl_fork(); if ($pid < 0) { exit('fork error!'); } else if ($pid == 0) { cli_set_process_title('gamelog:worker'); while (true) { //do your work usleep(100); } exit(); } else { $aPid[] = $pid; } } }else if($daemonNum > $workerNum){ $wokerNum = max($wokerNum, PROC_MIN); $killNum = $daemonNum - $workerNum; foreach($aPid as $key=>$pid){ if(posix_kill($pid, SIGKILL)){ unset($aPid[$key]); if(--$killNum <= 0){ break; } } } } }, false); pcntl_signal(SIGUSR1, function() use (&$aPid, $pid){ foreach($aPid as $key=>$chpid){ if(!posix_kill($chpid, SIGKILL)){ echo "kill child $chpid faild\n"; } } posix_kill($pid, SIGKILL); }, false); pcntl_signal(SIGUSR2, function() use (&$aPid, $pid){ foreach($aPid as $key=>$chpid){ if(!posix_kill($chpid, SIGKILL)){ echo "kill child $chpid faild\n"; } } if(!posix_kill($pid, SIGALRM)){ echo "restart gamelog faild\n"; } }, false); posix_kill($pid, SIGALRM); while (true) { pcntl_signal_dispatch(); $pid = pcntl_wait($status, WUNTRACED);//不阻塞 } break; case 'stop' : if(!posix_kill($pid, SIGUSR1)){ exit('stop gamelog process error!'); } break; case 'reload' : if(!posix_kill($pid, SIGUSR2)){ exit('restop gamelog process error!'); } break; default : echo "Useage php signal.php start|stop|reload\n"; }
以上所述是小編給大家介紹的PHP進(jìn)程管理詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- PHP如何限制定時(shí)任務(wù)的進(jìn)程數(shù)量
- PHP基于進(jìn)程控制函數(shù)實(shí)現(xiàn)多線程
- PHP程序守護(hù)進(jìn)程化實(shí)現(xiàn)方法詳解
- 一文看懂PHP進(jìn)程管理器php-fpm
- php多進(jìn)程中的阻塞與非阻塞操作實(shí)例分析
- php多進(jìn)程并發(fā)編程防止出現(xiàn)僵尸進(jìn)程的方法分析
- PHP多進(jìn)程通信-消息隊(duì)列使用
- php多進(jìn)程模擬并發(fā)事務(wù)產(chǎn)生的問(wèn)題小結(jié)
- 淺談并發(fā)處理PHP進(jìn)程間通信之外部介質(zhì)
相關(guān)文章
Laravel框架中VerifyCsrfToken報(bào)錯(cuò)問(wèn)題的解決
這篇文章主要給大家介紹了關(guān)于Laravel框架中VerifyCsrfToken報(bào)錯(cuò)問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí),需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08Codeigniter整合Tank Auth權(quán)限類(lèi)庫(kù)詳解
相交其他CodeIgniter的類(lèi)庫(kù),tank_auth,配置簡(jiǎn)單,使用也簡(jiǎn)單,并且作者也一直在更新。這篇文章主要介紹了Codeigniter整合Tank Auth權(quán)限類(lèi)庫(kù)詳解,需要的朋友可以參考下2014-06-06Laravel框架環(huán)境與配置操作實(shí)例分析
這篇文章主要介紹了Laravel框架環(huán)境與配置操作,結(jié)合實(shí)例形式分析了laravel框架基本環(huán)境配置方法及維護(hù)模式相關(guān)操作技巧,需要的朋友可以參考下2019-12-12Laravel5.2使用Captcha生成驗(yàn)證碼實(shí)現(xiàn)登錄(session巨坑)
這篇文章主要介紹了Laravel5.2使用Captcha生成驗(yàn)證碼(session巨坑),需要的朋友可以參考下2018-01-01php結(jié)合GD庫(kù)實(shí)現(xiàn)中文驗(yàn)證碼的簡(jiǎn)單方法
這篇文章主要給大家介紹了關(guān)于php結(jié)合GD庫(kù)實(shí)現(xiàn)中文驗(yàn)證碼的簡(jiǎn)單方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01關(guān)于PHP的相似度計(jì)算函數(shù):levenshtein的使用介紹
本篇文章小編將為大家介紹,關(guān)于PHP的相似度計(jì)算函數(shù) levenshtein的使用介紹,有需要的朋友可以參考一下2013-04-04詳細(xì)Laravel5.5執(zhí)行表遷移命令出現(xiàn)表為空的解決方案
這篇文章主要介紹了詳細(xì)Laravel5.5執(zhí)行表遷移命令出現(xiàn)表為空的解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07