【多进程】php实现 master-worker 守护多进程模式
<?php class Worker{ public static $count = 2; public static function runAll(){ static::runMaster(); static::moniProcess(); } //开启主进程 public static function runMaster(){ //确保进程有最大操作权限 unmask(0); $pid = pcntl_fork(); if($pid > 0){ echo "主进程进程 $pid \n"; exit; }else if($pid == 0){ if(-1 === posix_setsid()){ throw new Exception("setsid fail"); } for ($i=0; $i < self::$count; $i++) { static::runWorker(); } @cli_set_process_title("master_process"); }else{ throw new Exception("创建主进程失败"); } } //开启子进程 public static function runWorker(){ unmask(0); $pid = pcntl_fork(); if($pid > 0){ // echo "创建子进程 $pid \n"; }else if($pid == 0){ if(-1 === posix_setsid()){ throw new Exception("setsid fail"); } @cli_set_process_title("worker_process"); while(1){ sleep(1); } }else{ throw new Exception("创建子进程失败"); } } //监控worker进程 public static function moniProcess(){ while( $pid = pcntl_wait($status)){ if($pid == -1){ break; }else{ static::runWorker(); } } } } Worker::runAll();
ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 18200 3076 pts/0 Ss+ 14:05 0:00 bash root 6 0.0 0.0 18208 3252 pts/1 Ss 14:06 0:00 bash root 19 0.0 0.0 18204 3248 pts/2 Ss+ 14:11 0:00 bash root 64 0.0 0.2 348488 8320 ? Ss 15:32 0:00 master_process root 65 0.0 0.2 348488 8400 ? Ss 15:32 0:00 worker_process root 66 0.0 0.2 348488 8400 ? Ss 15:32 0:00 worker_process root 67 0.0 0.0 36640 2804 pts/1 R+ 15:32 0:00 ps -aux
执行命令 kill 65,杀死进程 65 则master_process 进程会再自动开启一个子进程
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 18200 3076 pts/0 Ss+ 14:05 0:00 bash root 6 0.0 0.0 18208 3252 pts/1 Ss 14:06 0:00 bash root 19 0.0 0.0 18204 3248 pts/2 Ss+ 14:11 0:00 bash root 64 0.0 0.2 348488 8320 ? Ss 15:32 0:00 master_process root 66 0.0 0.2 348488 8400 ? Ss 15:32 0:00 worker_process root 68 0.0 0.1 348488 5796 ? Ss 15:34 0:00 worker_process root 69 0.0 0.0 36640 2728 pts/1 R+ 15:34 0:00 ps -aux
信号处理增强版
<?php declare(ticks=1); class Worker { public $count = 2; public $child_pid = []; public $is_moni = true; public function runAll() { $this->runMaster(); $this->moniProcess(); } //开启主进程 public function runMaster() { //确保进程有最大操作权限 // unmask(0); $pid = pcntl_fork(); if ($pid > 0) { echo "主进程进程 $pid \n"; exit; } else if ($pid == 0) { if (-1 === posix_setsid()) { // ps -axO pgid throw new Exception("setsid fail"); } for ($i = 0; $i < $this->count; $i++) { $this->runWorker(); } @cli_set_process_title("master_process"); pcntl_signal(SIGUSR1, array($this, 'signalHandler')); } else { throw new Exception("创建主进程失败"); } } //开启子进程 public function runWorker() { // unmask(0); $pid = pcntl_fork(); if ($pid > 0) { $this->child_pid[$pid] = 1; echo "创建子进程 $pid \n"; } else if ($pid == 0) { if (-1 === posix_setsid()) { throw new Exception("setsid fail"); } @cli_set_process_title("worker_process"); while (1) { sleep(1); } } else { throw new Exception("创建子进程失败"); } } //监控worker进程 public function moniProcess() { while (true) { $pid = pcntl_wait($status, WNOHANG); if ($pid > 0 && $this->is_moni) { $this->runWorker(); } elseif ($pid > 0 && !$this->is_moni) { //主进程发起退出 unset($this->child_pid[$pid]); if (!count($this->child_pid)) { exit; } } else { continue; } sleep(1); } } //收到信号回调 public function signalHandler($signo) { switch ($signo) { case SIGUSR1: file_put_contents('sign.txt', json_encode($this->child_pid), FILE_APPEND); $this->is_moni = false; foreach ($this->child_pid as $key => $value) { posix_kill($key, SIGKILL); } break; } } } (new Worker)->runAll();
运行起来之后执行以下命令可以关闭守护进程
kill -10 主进程id