PHP 进程的实现与管理
應(yīng)用場景
一些耗時(shí)任務(wù):
所以我們就需要一個(gè)常駐內(nèi)存的任務(wù)管理工具,為了保證實(shí)時(shí)性,一方面我們讓它一直執(zhí)行任務(wù)(適當(dāng)?shù)乃?#xff0c;保證cpu不被100%占用),另一方面我們實(shí)現(xiàn)多進(jìn)程保證并發(fā)的執(zhí)行任務(wù),當(dāng)然除此之外也可按情況使用線程、協(xié)程實(shí)現(xiàn)。
運(yùn)行模式
實(shí)現(xiàn)PHP進(jìn)程前,需了解常見的php的運(yùn)行模式:
而php進(jìn)程則是使用CLI命令行模式運(yùn)行的
基本實(shí)現(xiàn)
PHP中提供了一個(gè)擴(kuò)展pcntl,可以利用操作系統(tǒng)的fork調(diào)用來實(shí)現(xiàn)多進(jìn)程。fork調(diào)用后執(zhí)行的代碼將是并行的,且只能在linux下運(yùn)行。
$ppid = posix_getpid();// 獲取當(dāng)前進(jìn)程PID $pid = pcntl_fork(); //創(chuàng)建進(jìn)程switch ($pid){// 創(chuàng)建進(jìn)程錯(cuò)誤case -1:throw new Exception('fork子進(jìn)程失敗!');break;// 子進(jìn)程workercase 0:$cpid = posix_getpid();cli_set_process_title("我是{$ppid}的子進(jìn)程,我的進(jìn)程id是{$cpid}.");sleep(30);exit; // 這里exit掉,避免worker繼續(xù)執(zhí)行下面的代碼而造成一些問題break;// 主進(jìn)程masterdefault:cli_set_process_title("我是父進(jìn)程,我的進(jìn)程id是{$ppid}.");pcntl_wait($status); // 掛起父進(jìn)程,等待并返回子進(jìn)程狀態(tài),防止子進(jìn)程成為僵尸進(jìn)程break; }在命令行php xxx.php運(yùn)行后,使用ps aux | grep 進(jìn)程可以看到:
如果沒看到,可能是中文亂碼了,使用ps aux,查看
或者使用ps –ajft查看層次顯示
進(jìn)程管理-防止進(jìn)程成為僵尸進(jìn)程
創(chuàng)建好了進(jìn)程,那么怎么對子進(jìn)程進(jìn)行管理呢?使用信號(hào),對子進(jìn)程的管理,一般有兩種情況:
posix_kill():此函數(shù)并不能顧名思義,它通過向子進(jìn)程發(fā)送一個(gè)信號(hào)來操作子進(jìn)程,在需要要時(shí)可以選擇給子進(jìn)程發(fā)送進(jìn)程終止信號(hào)來終止子進(jìn)程;
pcntl_waitpid():等待或返回fork的子進(jìn)程狀態(tài),如果指定的子進(jìn)程在此函數(shù)調(diào)用時(shí)已經(jīng)退出(俗稱僵尸進(jìn)程),此函數(shù)將立刻返回,并釋放子進(jìn)程的所有系統(tǒng)資源,此進(jìn)程可以避免子進(jìn)程變成僵尸進(jìn)程,造成系統(tǒng)資源浪費(fèi);
孤兒進(jìn)程:父進(jìn)程掛了,子進(jìn)程被pid=1的init進(jìn)程接管(wait/waitpid),直到子進(jìn)程自身生命周期結(jié)束被系統(tǒng)回收資源和父進(jìn)程 采取相關(guān)的回收操作
僵尸進(jìn)程:子進(jìn)程exit退出,父進(jìn)程沒有通過wait/waitpid獲取子進(jìn)程狀態(tài),子進(jìn)程占用的進(jìn)程號(hào)等描述資源符還存在,產(chǎn)生危害:例如進(jìn)程號(hào)是有限的,無法釋放進(jìn)程號(hào)導(dǎo)致未來可能無進(jìn)程號(hào)可用
**父進(jìn)程中使用:pcntl_wait或者pcntl_waitpid的目的就是防止worker成為僵尸進(jìn)程
作用:使用pcntl_wait()后,在子進(jìn)程死掉后,父進(jìn)程也會(huì)被停止**
最后我們通過下圖(1-1)來簡單的總結(jié)和描述這個(gè)多進(jìn)程實(shí)現(xiàn)的過程:
進(jìn)程管理-進(jìn)程間通信
隊(duì)列:如Redis,推薦
socket:推薦
管道:實(shí)現(xiàn)復(fù)雜,且管道(pipe),使用文件形式存在,存在硬盤IO性能瓶頸
信號(hào):承載信息量少,不好管理
進(jìn)程管理-切換為守護(hù)進(jìn)程
使用&實(shí)現(xiàn)
php deadloop.php &
實(shí)際多進(jìn)程的使用
一個(gè)耗時(shí)10S的任務(wù),執(zhí)行2次,總耗時(shí)20S,而開2個(gè)進(jìn)程,只需10S,如下:
job.php:index.php(進(jìn)程開啟腳本): echo '開始時(shí)間:'.date('H:i:s', time())."\n";$cmds = [['./job.php', 0, 50000],//執(zhí)行腳本,并傳參['./job.php', 50000, 100000] ]; for ($i = 0; $i < 2; $i++){$ppid = posix_getpid();// 獲取當(dāng)前進(jìn)程PID$pid = pcntl_fork(); //創(chuàng)建進(jìn)程switch ($pid){// 創(chuàng)建進(jìn)程錯(cuò)誤case -1:throw new Exception('fork子進(jìn)程失敗!');break;// 子進(jìn)程workercase 0:$cpid = posix_getpid();cli_set_process_title("我是{$ppid}的子進(jìn)程,我的進(jìn)程id是{$cpid}.");// 執(zhí)行業(yè)務(wù)腳本pcntl_exec('/usr/local/php/bin/php', $cmds[$i]);exit; // 這里exit掉,避免worker繼續(xù)執(zhí)行下面的代碼而造成一些問題break;} }// 等待子進(jìn)程結(jié)束 while (pcntl_waitpid(0, $status) != -1) {$status = pcntl_wexitstatus($status);echo '子進(jìn)程結(jié)束時(shí)間:'.date('H:i:s', time())."\n"; }
運(yùn)行php index.php后:
實(shí)例達(dá)到理想效果。
總結(jié)
以上是生活随笔為你收集整理的PHP 进程的实现与管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Jmeter】参数Parameters
- 下一篇: PHP大文件分片上传