php实现 master-worker 守护多进程模式的实例代码

 
更多

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();
打赏

本文固定链接: https://www.cxy163.net/archives/2970 | 绝缘体

该日志由 绝缘体.. 于 2024年05月11日 发表在 未分类 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: php实现 master-worker 守护多进程模式的实例代码 | 绝缘体
关键字: , , , ,

php实现 master-worker 守护多进程模式的实例代码:等您坐沙发呢!

发表评论


快捷键:Ctrl+Enter