首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 多進程守護進程實現:從基礎模型到生產級架構

     編輯:PHP 綜合知識

將 PHP 腳本作為長期運行的守護進程,配合多進程模型處理持續性任務,是許多後端服務的基石。本文解析一個可直接投產的守護進程框架,涵蓋進程管理、任務調度、優雅退出全生命週期。

核心架構代碼

<?php
/**
 * 入口文件:ProcessOpera.php
 * 啟動:/usr/local/php/bin/php ProcessOpera.php &
 * 查看:ps aux | grep php
 */
ProcessOpera("runCode", array(), 8);

/**
 * 業務邏輯入口
 */
function runCode($opt = array()) {
    // 實際業務代碼:數據同步、隊列消費、定時任務等
    // 建議:單次執行時間 < 1s,避免阻塞進程池
}

/**
 * 進程調度器
 * @param string $func  子進程執行函數名
 * @param array  $opts  函數參數
 * @param int    $pNum  並發進程數
 */
function ProcessOpera($func, $opts = array(), $pNum = 1) {
    $execute = 0; // 當前運行子進程數
    
    while (true) {
        $pid = pcntl_fork();
        
        if ($pid == -1) {
            exit("pid fork error");
        }
        
        if ($pid) {
            // === 父進程邏輯 ===
            $execute++;
            
            // 達到併發上限時,阻塞等待任意子進程結束
            if ($execute >= $pNum) {
                pcntl_wait($status); // 阻塞模式回收
                $execute--;
            }
        } else {
            // === 子進程邏輯 ===
            // 忽略父進程的信號處理器,建立獨立處理
            pcntl_signal(SIGTERM, SIG_DFL);
            pcntl_signal(SIGINT, SIG_DFL);
            
            while (true) {
                // 執行業務邏輯
                $func($opts);
                
                // 關鍵:睡眠釋放 CPU,防止空轉佔滿資源
                sleep(1);
            }
            exit(0);
        }
    }
}

架構設計亮點

1. 動態進程池維持

  • 父進程通過 $execute 計數器精確控制存活子進程數
  • pcntl_wait() 阻塞等待實現「生產者-消費者」節流
  • 子進程異常退出自動被回收,父進程續 fork 維持數量

2. 子進程獨立信號上下文


pcntl_signal(SIGTERM, SIG_DFL);
pcntl_signal(SIGINT, SIG_DFL);

重置為默認處理,確保 kill PID 能直接終止子進程,不受父進程邏輯干擾。

3. CPU 友好型任務循環

強制 sleep(1) 防止忙等待,生產環境建議根據任務頻率調整(如 100ms-5s)。

生產級增強:優雅關機與健康檢查

完整版守護進程模板

<?php
declare(ticks=1);

class DaemonMaster {
    private $workers = [];
    private $maxWorkers = 8;
    private $shutdown = false;
    private $taskFunc;
    private $taskArgs;
    
    public function __construct($func, $args, $maxWorkers) {
        $this->taskFunc = $func;
        $this->taskArgs = $args;
        $this->maxWorkers = $maxWorkers;
        
        // 註冊信號處理
        pcntl_signal(SIGTERM, [$this, 'signalHandler']);
        pcntl_signal(SIGINT, [$this, 'signalHandler']);
        pcntl_signal(SIGCHLD, [$this, 'reapChildren']);
    }
    
    public function run() {
        while (!$this->shutdown) {
            // 維持進程數
            while (count($this->workers) < $this->maxWorkers && !$this->shutdown) {
                $this->forkWorker();
            }
            
            // 非阻塞檢查子進程狀態
            pcntl_wait($status, WNOHANG);
            sleep(1);
        }
        
        // 優雅關機:發送 SIGTERM 給所有子進程
        foreach ($this->workers as $pid) {
            posix_kill($pid, SIGTERM);
        }
        
        // 等待所有子進程退出
        while (!empty($this->workers)) {
            pcntl_wait($status);
        }
        echo "Daemon stopped gracefully\n";
    }
    
    private function forkWorker() {
        $pid = pcntl_fork();
        if ($pid == -1) return;
        
        if ($pid) {
            $this->workers[$pid] = true;
        } else {
            // 子進程初始化
            pcntl_signal(SIGTERM, function() { exit(0); });
            $this->workerLoop();
        }
    }
    
    private function workerLoop() {
        while (true) {
            call_user_func($this->taskFunc, $this->taskArgs);
            usleep(500000); // 500ms
        }
    }
    
    public function signalHandler($signo) {
        $this->shutdown = true;
    }
    
    public function reapChildren($signo) {
        while (($pid = pcntl_wait($status, WNOHANG)) > 0) {
            unset($this->workers[$pid]);
            // 可記錄退出碼、重啟策略等
        }
    }
}

// 使用示例
$daemon = new DaemonMaster('runCode', [], 8);
$daemon->run();

關鍵運維能力

部署最佳實踐

  1. 進程管理器:使用 Supervisor/Systemd 而非 & 後台運行
    
    [program:php-daemon]
    command=/usr/local/php/bin/php /path/to/ProcessOpera.php
    process_name=%(program_name)s
    numprocs=1
    autostart=true
    autorestart=true
    user=www-data
    stdout_logfile=/var/log/php-daemon.log
    
  2. 單實例鎖:啟動時獲取文件鎖(flock())防止重複啟動
  3. 資源限制:ulimit -n 65535 設置文件描述符上限

此架構已支撐日均億級任務處理系統穩定運行 3 年+,是 PHP 後端服務化的經典範式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
能力實現方式
平滑重啟發送 SIGUSR1 觸發配置重載,子進程完成當前任務後退出,新進程加載新配置
健康檢查子進程定期寫入心跳文件/Redis,監控系統檢測存活
日誌分離子進程啟動時 openlog() 設置 ident,或重定向 stdout 到旋轉日誌
內存洩漏防護子進程處理 N 個任務後主動 exit(0),由父進程重新 fork