PHP使用守護進程處理隊列


一.概述

  項目是棋牌,web架構是典型的lnmp,server產生的牌局通過http協議請求webserver,由php分析並持久化到mysql,中間參雜了很多業務邏輯,整個流程耗時平均接近2s。
  這種方式存在以下2個問題
  1.整個流程是同步的,server會一直等待php響應,一旦server處理不慎,會造成server阻塞,玩家無法玩牌。
  2.如果牌局數量較多,會占用較多的php-fpm進程,可能造成php-fpm無法處理其他業務。

 

二.改進方式

  后面改由server把牌局數據寫到redis隊列里,php使用守護進程處理redis隊列。
  cron每5分鍾運行gamelog.php,gamelog檢測牌局隊列數量,根據隊列的數量動態fork對應的子進程處理牌局業務,當子進程數量有多余的空閑進程,gamelog.php
  會殺掉多余的進程,這種方式參考了php-fpm的dynamic模式,具體實現如下:

define('LEN', 50);//單進程處理牌局隊列長度
define('PROC_MIN', 2);//最小進程數
define('PROC_MAX', 5);//最大進程數
ini_set('memory_limit', '128M');
umask(002);
set_time_limit(0);//cli模式非必須

$daemonNum = (int) `ps -ef | grep "gamelog.php" | grep -v grep | awk '$3 == 1 {print $2}' | wc -l`;//當前守護進程數
$appGameLog = app::gamelog();
$appRedis = app::redis('log');
$key = akey::gamelog();
$len = $appRedis->lSize($key);
$wokerNum = ceil($len / LEN);//需要的進程數
($wokerNum < PROC_MIN) && ($wokerNum = PROC_MIN);
if($daemonNum < $wokerNum){//守護進程數小於需要開啟的進程數
    $procNum = $wokerNum - $daemonNum;
    $procNum = min($procNum, PROC_MAX);
    $procNum = max($procNum, PROC_MIN);
    
    for($p = 1; $p <= $procNum; $p++){
        $pid = pcntl_fork();
        if($pid == 0){
            posix_setsid();
            while(true){
                $data = $appRedis->rPop($key);
                //此處處理業務
            }
            exit;
        }else if($pid > 0){
            
        }else{
            exit("fork error");
        }
    }
}else if($daemonNum > $wokerNum){//進程數過多自行kill
    $pidStr = `ps -ef | grep "gamelog.php" | grep -v grep | awk '$3 == 1 {print $2}'`;
    $aPid = explode(PHP_EOL, $pidStr);
    $wokerNum = max($wokerNum, PROC_MIN);//最少保留PROC_MIN個守護進程
    $killNum = $daemonNum - $wokerNum;
    foreach($aPid as $pid){
        $pid = (int) $pid;
        if($pid <= 0){
            continue;
        }
        if(posix_kill($pid, SIGKILL)){
            if(--$killNum <= 0){
                break;
            }
        }
    }
}

php執行shell命令除了system(),exec(),還可以使用``。
posix_setsid()函數php手冊里只有一句說明 Make the current process a session leader
posix_setsid對應的unix系統函數是setsid(),當進程調用setsid會產生一個新的會話,而且這個進程將不受終端控制
之前進程有終端控制也會被解除,所以我們在命令行啟動gamelog.php,然后關掉終端不會殺掉gamelog.php產生的子進程

 

三.改進后的效果
  1.改進后server寫redis隊列遠比通過http協議請求php快,極大減少了server等待牌局處理的時間。
  2.php-fpm不用處理牌局的請求,改由后台進程處理,釋放了php-fpm。

 

 

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3mj93mrrhoqos


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM