php实现 master-worker 守护多进程模式的实例代码。
<?php /** * Class Worker * php实现 master-worker 守护多进程模式的实例代码 * kill 主进程,自动杀掉子进程 * 子进程意外终止将会被守护进程重新创建 * @link https://www.phpernote.com/ * @author www.phpernote.com * @to do 判断进程是否已经启动,不允许重复启动 */ class Worker { private static $process_title = 'phpernote.com'; public static $count = 2; public static $job_iterations = 10; private static $child_pids = []; // 存储所有子进程的PID public static function run(): void { // 注册信号处理函数 pcntl_signal(SIGTERM, [__CLASS__, 'handleSignal']); pcntl_signal(SIGINT, [__CLASS__, 'handleSignal']); try { static::runMaster(); static::monitorWorkerProcess(); } catch (Exception $e) { echo "发生错误: " . $e->getMessage() . "\r\n"; } } /** * 开启主进程 * @throws Exception */ public static function runMaster(): void { umask(0); $pid = pcntl_fork(); if ($pid > 0) { echo "主进程进程 $pid\r\n"; exit; } else if ($pid == 0) { if (-1 === posix_setsid()) { throw new Exception('setsid fail'); } for ($i = 0; $i < self::$count; $i++) { $child_pid = static::runWorker(); self::$child_pids[] = $child_pid; // 存储子进程PID } cli_set_process_title(self::$process_title . 'master_process'); } else { throw new Exception('创建主进程失败'); } } /** * 开启子进程 * @throws Exception */ public static function runWorker() { umask(0); $pid = pcntl_fork(); if ($pid > 0) { echo "创建子进程 $pid \r\n"; return $pid; // 返回子进程PID给主进程存储 } else if ($pid == 0) { if (-1 === posix_setsid()) { throw new Exception('setsid fail'); } cli_set_process_title(self::$process_title . 'worker_process'); try { self::doJob(); } catch (Exception $e) { echo "Worker 进程发生错误: " . $e->getMessage() . "\r\n"; } exit; // 确保Worker完成任务后退出 } else { throw new Exception('创建子进程失败'); } } /** * 监控worker进程 * @throws Exception */ public static function monitorWorkerProcess(): void { while (true) { $pid = pcntl_wait($status); if ($pid == -1) { if ($errno = pcntl_wexitstatus($status)) { echo "子进程异常退出,错误码: $errno\r\n"; } break; } else { echo "子进程 $pid 退出,正在重新创建...\r\n"; static::runWorker(); } } } /** * 业务逻辑 */ public static function doJob(): void { for ($i = 0; $i < self::$job_iterations; $i++) { sleep(3); echo '进程:' . posix_getpid() . ' ' . date('Y-m-d H:i:s') . ' 输出' . "\r\n"; } echo "Worker 任务执行完毕\r\n"; } /** * 信号处理函数 * @param $signal * @return void */ public static function handleSignal($signal): void { echo "接收到信号 " . $signal . ",开始清理...\r\n"; foreach (self::$child_pids as $pid) { posix_kill($pid, SIGTERM); // 尝试优雅地终止子进程 posix_kill($pid, SIGKILL); // 如果优雅终止失败,则强制终止 } exit; // 清理完成后,终止主进程 } } Worker::run();
经过 chatGPT 优化后的代码如下:
<?php /** * Class Worker * php实现 master-worker 守护多进程模式的实例代码 * kill 主进程,自动杀掉子进程 * 子进程意外终止将会被守护进程重新创建 * @link https://www.phpernote.com/ * @author www.phpernote.com * @to do 判断进程是否已经启动,不允许重复启动 */ class Worker { private static $process_title_master = 'phpernote_master'; private static $process_title_child = 'phpernote_child'; public static $count = 2; public static $job_iterations = 10; private static $child_pids = []; // 存储所有子进程的PID public static function run(): void { // 注册信号处理函数 pcntl_signal(SIGTERM, [__CLASS__, 'handleSignal']); pcntl_signal(SIGINT, [__CLASS__, 'handleSignal']); try { static::runMaster(); } catch (Exception $e) { echo "发生错误: " . $e->getMessage() . "\r\n"; } } /** * 开启主进程 * @throws Exception */ public static function runMaster(): void { umask(0); $pid = pcntl_fork(); if ($pid > 0) { echo "主进程进程 $pid\r\n"; cli_set_process_title(self::$process_title_master); static::runWorkers(); static::monitorWorkerProcess(); } else if ($pid == 0) { if (-1 === posix_setsid()) { throw new Exception('setsid fail'); } cli_set_process_title(self::$process_title_child); } else { throw new Exception('创建主进程失败'); } } /** * 开启子进程 * @throws Exception */ public static function runWorkers(): void { for ($i = 0; $i < self::$count; $i++) { $child_pid = static::runWorker(); self::$child_pids[] = $child_pid; // 存储子进程PID } } /** * 开启单个子进程 * @throws Exception */ public static function runWorker() { umask(0); $pid = pcntl_fork(); if ($pid > 0) { echo "创建子进程 $pid \r\n"; return $pid; // 返回子进程PID给主进程存储 } else if ($pid == 0) { if (-1 === posix_setsid()) { throw new Exception('setsid fail'); } cli_set_process_title(self::$process_title_child); try { self::doJob(); } catch (Exception $e) { echo "Worker 进程发生错误: " . $e->getMessage() . "\r\n"; } exit; // 确保Worker完成任务后退出 } else { throw new Exception('创建子进程失败'); } } /** * 监控worker进程 * @throws Exception */ public static function monitorWorkerProcess(): void { while (true) { $pid = pcntl_wait($status); if ($pid == -1) { if ($errno = pcntl_wexitstatus($status)) { echo "子进程异常退出,错误码: $errno\r\n"; } break; } else { echo "子进程 $pid 退出,正在重新创建...\r\n"; // 从数组中删除已退出的子进程 PID $index = array_search($pid, self::$child_pids); if ($index !== false) { unset(self::$child_pids[$index]); } static::runWorker(); } } } /** * 业务逻辑 */ public static function doJob(): void { for ($i = 0; $i < self::$job_iterations; $i++) { sleep(3); echo '进程:' . posix_getpid() . ' ' . date('Y-m-d H:i:s') . ' 输出' . "\r\n"; } echo "Worker 任务执行完毕\r\n"; } /** * 信号处理函数 * @param $signal * @return void */ public static function handleSignal($signal): void { echo "接收到信号 " . $signal . ",开始清理...\r\n"; foreach (self::$child_pids as $pid) { posix_kill($pid, SIGTERM); // 尝试优雅地终止子进程 // 一段时间后再尝试强制终止 pcntl_alarm(5); // 5秒后发送SIGKILL } } } Worker::run();