PHP使用守护进程处理队列

发布日期:2019-01-05

一.概述

  项目是棋牌web架构是典型的lnmpserver产生的牌局通过http协议请求webserver由php分析并持久化到mysql中间参杂了很多业务逻辑整个流程耗时平均接近2s。  这种方式存在以下2个问题  1.整个流程是同步的,server会一直等待php响应一旦server处理不慎,会造成server阻塞,玩家无法玩牌。  2.如果牌局数量较多会占用较多的php-fpm进程可能造成php-fpm无法处理其他业务。

 

二.改进方式

  后面改由server把牌局数据写到redis队列里php使用守护进程处理redis队列。  cron每5分钟运行gamelog.phpgamelog检测牌局队列数量根据队列的数量动态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 leaderposix_setsid对应的unix系统函数是setsid()当进程调用setsid会产生一个新的会话而且这个进程将不受终端控制之前进程有终端控制也会被解除所以我们在命令行启动gamelog.php然后关掉终端不会杀掉gamelog.php产生的子进程

 

三.改进后的效果  1.改进后server写redis队列远比通过http协议请求php快,极大减少了server等待牌局处理的时间。  2.php-fpm不用处理牌局的请求,改由后台进程处理,释放了php-fpm。