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 tprocessjingwu ??10126 ?4844 ?0 00:16 pts/5 ???00:00:00 tprocess-masterjingwu ??10127 10126 ?0 00:16 pts/5 ???00:00:00 tprocess-0jingwu ??10128 10126 ?0 00:16 pts/5 ???00:00:00 tprocess-1jingwu ??10129 10126 ?0 00:16 pts/5 ???00:00:00 tprocess-2jingwu ??10130 10126 ?0 00:16 pts/5 ???00:00:00 tprocess-3jingwu ??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 stopEOF;}
PHP实现多进程
原文地址:http://blog.51cto.com/jingwu/2064035