PHP進程管理的程式碼範例

不言
發布: 2023-04-04 12:32:01
轉載
1874 人瀏覽過

這篇文章帶給大家的內容是關於PHP進程管理的程式碼範例,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

這篇文章是對之前一篇文章的補充和改進, 創建一個主(master)進程,主進程安裝定時器,每隔5分鐘檢測一次隊列長度,根據隊列長度計算需要的worker進程,

然後創建或殺死子進程。這樣做的好處是防止隊列堆積,任務無法及時處理。更新業務代碼,只需要reload操作即可。

整個流程有以下知識點:

  • 建立守護程式的步驟:

  1. 設定預設檔案權限

  2. fork一個進程,父進程退出

  3. #呼叫setsid建立一個新的會話

  4. 將目前工作目錄變更為根目錄

  5. 關閉不再需要的檔案描述子

  • ##使用訊號實現定時器

上一篇定時器依賴系統的定時任務,這次使用鬧鐘訊號實現,php 5.3.0以下的版本依賴ticks,5.3.0及以上版本可使用pcntl_signal_dispatch

訊號:提供了一種非同步事件處理的方法,當某個訊號出現時,流程有以下三種方式處理訊號

  1. #忽略此訊號

  2. 捕捉訊號

  3. 執行系統預設動作,大多數訊號的預設動作是終止該程序

執行系統預設動作,大多數訊號的預設動作是終止該程序
#常見訊號

SIGKILL,SIGSTOP是兩個無法被使用者忽略和捕捉的訊號

SIGINT( 2):程序終止訊號,通常是Ctrl-C)時發出,用於通知前台進程組終止進程

SIGQUIT(3):和SIGINT類似, 但由QUIT字元(通常是Ctrl /)來控制. 進程收到該訊息退出時會產生core檔案

SIGKILL(9):立即終止進程,不可被忽略捕捉或阻塞

SIGUSR1(10):使用者定義訊號

SIGUSR2(12):留給使用者使用

SIGALRM(14):鬧鐘訊號

SIGTERM(15):終止進程,可被程式捕捉,使得進程可以執行完清理操作。
  • SIGSTOP(19):停止一個行程,該行程還未結束, 只是暫停執行

防止產生殭屍行程

所有的行程在退出的時候都會變成殭屍行程,這時候如果父行程還在執行,沒有呼叫wait或waitpid,則殭屍行程佔用的資源不會被清理,如果父行程已終止,殭屍行程由init進程進行清理。

抽調業務程式碼,主要程式碼如下  

#其中要注意的一點,建立守護程式關閉輸入輸出,錯誤輸出流的時候,如果程式碼後面有echo等輸出字符,將出現致命錯誤,需要在php程式碼中重定向輸出流到/dev/null。或是在終端啟動程序的時候進行重定向

###
<?php
define(&#39;PROC_MAX&#39;, 10);
define(&#39;PROC_MIN&#39;, 5);

$cmd = $argv[1];
$aPid = [];
$pidFile = __DIR__ . &#39;/pid.pid&#39;;
$pid = file_get_contents($pidFile);

switch($cmd){
    case &#39;start&#39; :
        if(posix_kill($pid, 0)){
            echo "gamelog process is already exsits!\n";
            return false;
        }
        //设置默认文件权限
        umask(022);
        //fork
        $pid = pcntl_fork();
        if($pid < 0){
            exit(&#39;fork error!&#39;);
        }else if($pid > 0){
            exit;
        }
        //脱离当前终端
        posix_setsid();
        //将当前工作目录更改为根目录
        chdir(&#39;/&#39;);
        //关闭文件描述符
        fclose(STDIN);
        fclose(STDOUT);
        fclose(STDERR);
        //重定向输入输出
        global $STDOUT, $STDERR;
        $STDOUT = fopen(&#39;/dev/null&#39;, &#39;a&#39;);
        $STDERR = fopen(&#39;/dev/null&#39;, &#39;a&#39;);
        
        cli_set_process_title(&#39;gamelog:master&#39;);
        $pid = posix_getpid();
        file_put_contents($pidFile, $pid);
        //闹钟信号
        pcntl_signal(SIGALRM, function() use (&$aPid) {
            pcntl_alarm(300);
            $workerNum = mt_rand(1, 20);//此处检测你需要的进程数
            $daemonNum = count($aPid);
            
            ($workerNum > PROC_MAX) && ($workerNum = PROC_MAX);
            if($daemonNum < $workerNum){
                $procNum = $workerNum - $daemonNum;
                $procNum = max(PROC_MIN, $procNum);
                for($p = 1; $p <= $procNum; $p++){
                    $pid = pcntl_fork();
                    if ($pid < 0) {
                        exit(&#39;fork error!&#39;);
                    } else if ($pid == 0) {
                        cli_set_process_title(&#39;gamelog:worker&#39;);
                        while (true) {
                            //do your work
                            usleep(100);
                        }
                        exit();
                    } else {
                        $aPid[] = $pid;
                    }
                }
            }else if($daemonNum > $workerNum){
                $wokerNum = max($wokerNum, PROC_MIN);
                $killNum = $daemonNum - $workerNum;
                foreach($aPid as $key=>$pid){
                    if(posix_kill($pid, SIGKILL)){
                        unset($aPid[$key]);
                        if(--$killNum <= 0){
                            break;
                        }
                    }
                }
            }
        }, false);
        
        pcntl_signal(SIGUSR1, function() use (&$aPid, $pid){
            foreach($aPid as $key=>$chpid){
                if(!posix_kill($chpid, SIGKILL)){
                    echo "kill child $chpid faild\n";
                }
            }
            posix_kill($pid, SIGKILL);
        }, false);
        
        pcntl_signal(SIGUSR2, function() use (&$aPid, $pid){
           foreach($aPid as $key=>$chpid){
                if(!posix_kill($chpid, SIGKILL)){
                    echo "kill child $chpid faild\n";
                }
            }
            if(!posix_kill($pid, SIGALRM)){
                echo "restart gamelog faild\n";
            }
        }, false);
        
        posix_kill($pid, SIGALRM);
        while (true) {
            pcntl_signal_dispatch();
            $pid = pcntl_wait($status, WUNTRACED);//不阻塞
        }
        break;
    
    case &#39;stop&#39; :
        if(!posix_kill($pid, SIGUSR1)){
            exit(&#39;stop gamelog process error!&#39;);
        }
        break;
    case &#39;reload&#39; :
        if(!posix_kill($pid, SIGUSR2)){
            exit(&#39;restop gamelog process error!&#39;);
        }
        break;
    default :
        echo "Useage php signal.php start|stop|reload\n";
}
登入後複製
#######

以上是PHP進程管理的程式碼範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:cnblogs.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板