PHP實現多進程
阿新 • • 發佈:2018-01-23
subst run 處理 進程狀態 itl 資源 ech pre usr swoole 實現php多進程同步
PHP 本身是一個強領域的語言,主要應用於web開發。 PHP 也可以進行多進程開發,但是使用的第三方擴展。 下面我們演示使用 swoole 實現 PHP多進程,且自定義進程名稱,可啟動及停止。 自定義進程名前綴:tprocess- 主進程名為:tprocess-master 子進程名為:tprocess-xxx xxx 為數字 停止進程有多種方式,比如 kill -9 強制殺死,但這樣會導致任務在中間中斷,出現臟數據 我們使用信號的方式,通知子進程,在下一個任務時退出。 在使用多進程時,容易出現僵屍進程,為了避免僵屍進程,我們使用 信號接收子進程退消息,並回收資源。
運行
nohup php -f tproc.php start &
停止,會發送信號給運行的主進程,由主進程通知子進程結束
php -f tproc.php stop
啟動後效果如下:
[jingwu@master test]$ ps -ef | grep tprocess jingwu 10126 4844 0 00:16 pts/5 00:00:00 tprocess-master jingwu 10127 10126 0 00:16 pts/5 00:00:00 tprocess-0 jingwu 10128 10126 0 00:16 pts/5 00:00:00 tprocess-1 jingwu 10129 10126 0 00:16 pts/5 00:00:00 tprocess-2 jingwu 10130 10126 0 00:16 pts/5 00:00:00 tprocess-3 jingwu 10131 10126 0 00:16 pts/5 00:00:00 tprocess-4
tproc.php
error_reporting(E_ALL); ini_set(‘display_errors‘, ‘1‘); ini_set(‘error_log‘, ‘/tmp/tprocess_error.log‘); define(‘PROC_LIMIT‘, 5); class ProcConfig { static $status = null; } //查找對應的進程,我們直接查找系統 /proc 目錄下的文件 //這樣做是因為,在生產環境不一定會開放 exec 等函數,有一定的局限 //查找系統文件還可以避免維護進程ID文件 function proc_find() { $baseDir = ‘/proc‘; $rows = scandir(‘/proc‘); $procTitles = []; foreach($rows as $pid) { if(!is_numeric($pid)) continue; $cmdlineFile = sprintf("%s/%s/cmdline", $baseDir, $pid); if(!file_exists($cmdlineFile)) continue; $title = trim(file_get_contents($cmdlineFile)); if(substr($title, 0, 9) != ‘tprocess-‘) continue; $procTitles[$title] = $pid; } return $procTitles; } //檢查子進程,因異常或任務結束而導致子進程結束 //通過檢查,維持一定數量的子進程 function proc_check() { $titles = [‘ip‘ => [], ‘domain‘ => []]; for($i = 0; $i < PROC_LIMIT; $i++) $titles[‘ip‘][] = [‘tprocess-‘.$i, $i]; $procs = proc_find(); foreach($titles as $key => $items) { foreach($items as $row) { $title = $row[0]; $index = $row[1]; if(isset($procs[$title])) continue; $worker = new swoole_process(function (swoole_process $process) use($index) {proc_child($process, $index);}, true); $procid = $worker->start(); } } } //守護進程,負責啟動及監控子進程 function proc_master() { $procs = proc_find(); if(isset($procs[‘tprocess-master‘])) { print("Dispatch[running]守護進程已經啟動\n"); exit(0); } //主進程異常結束,子進程未結束,結束所有子進程 if(!isset($procs[‘tprocess-master‘]) && $procs) { foreach($procs as $title => $pid) posix_kill($pid, 9); } //設置主進程名 swoole_set_process_name(‘tprocess-master‘); } //worker進程,負責處理數據 function proc_child(swoole_process $process, $index) { $title = ‘tprocess-‘.$index; $process->name($title); while(1) { if(ProcConfig::$status->get()) exit(1); sleep(5); } } $action = isset($argv[1]) ? $argv[1] : ‘‘; if($action == "start") { //進程狀態位,用於標識是否可以退出 ProcConfig::$status = new swoole_atomic(0); proc_master(); print("started worker\n"); sleep(2); proc_check(); //註冊子進程退出監聽函數,避免僵屍進程 swoole_process::signal(SIGCHLD, function($sig) { while ($ret = swoole_process::wait(false)) { file_put_contents(‘/tmp/tprocess_defunct.log‘, "child process killed: {$sig}-{$ret[‘pid‘]}\n", FILE_APPEND); proc_check(); } }); //主進程監聽 SIGUSR2 信號,並設置進程狀態位,通知子進程結束 swoole_process::signal(SIGUSR2, function($sig) { ProcConfig::$status->add(1); print("receive SIGUSR2 to stop worker\n"); exit(0); }); }else if($action == "stop") { $procs = proc_find(); if(isset($procs[‘tprocess-master‘])) { posix_kill($procs[‘tprocess-master‘], SIGUSR2); } print("stop disptch worker is ok\n"); exit(0); } else { echo <<< EOF cmd [action] action list: start stop eg: php -f ./tproc.php start php -f ./tproc.php stop EOF; }
PHP實現多進程