浅谈PHP进程管理


Posted in PHP onMarch 08, 2019

这篇文章是对之前一篇文章的补充和改进, 创建一个主(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('PROC_MAX', 10);
define('PROC_MIN', 5);
 
$cmd = $argv[1];
$aPid = [];
$pidFile = __DIR__ . '/pid.pid';
$pid = file_get_contents($pidFile);
 
switch($cmd){
 case 'start' :
  if(posix_kill($pid, 0)){
   echo "gamelog process is already exsits!\n";
   return false;
  }
  //设置默认文件权限
  umask(022);
  //fork
  $pid = pcntl_fork();
  if($pid < 0){
   exit('fork error!');
  }else if($pid > 0){
   exit;
  }
  //脱离当前终端
  posix_setsid();
  //将当前工作目录更改为根目录
  chdir('/');
  //关闭文件描述符
  fclose(STDIN);
  fclose(STDOUT);
  fclose(STDERR);
  //重定向输入输出
  global $STDOUT, $STDERR;
  $STDOUT = fopen('/dev/null', 'a');
  $STDERR = fopen('/dev/null', 'a');
   
  cli_set_process_title('gamelog:master');
  $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('fork error!');
     } else if ($pid == 0) {
      cli_set_process_title('gamelog:worker');
      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 'stop' :
  if(!posix_kill($pid, SIGUSR1)){
   exit('stop gamelog process error!');
  }
  break;
 case 'reload' :
  if(!posix_kill($pid, SIGUSR2)){
   exit('restop gamelog process error!');
  }
  break;
 default :
  echo "Useage php signal.php start|stop|reload\n";
}

以上所述是小编给大家介绍的PHP进程管理详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
PHP实现多条件查询实例代码
Jul 17 PHP
Base64在线编码解码实现代码 演示与下载
Jan 08 PHP
Windows下利用Gvim写PHP产生中文乱码问题解决方法
Apr 20 PHP
九个你必须知道而且又很好用的php函数和特点
Aug 08 PHP
深入解析php中的foreach函数
Aug 31 PHP
PHP获取客户端真实IP地址的5种情况分析和实现代码
Jul 08 PHP
浅谈PHP中Stream(流)
Jun 08 PHP
PHP表单提交后引号前自动加反斜杠的原因及三种办法关闭php魔术引号
Sep 30 PHP
php验证邮箱和ip地址最简单方法汇总
Oct 30 PHP
AJAX PHP无刷新form表单提交的简单实现(推荐)
Sep 09 PHP
Zend Framework入门教程之Zend_Registry组件用法详解
Dec 09 PHP
PHP常用排序算法实例小结【基本排序,冒泡排序,快速排序,插入排序】
Feb 07 PHP
PHP多进程通信-消息队列使用
Mar 08 #PHP
浅谈PHP匿名函数和闭包
Mar 08 #PHP
使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
Mar 08 #PHP
ThinkPHP中图片按比例切割的代码实例
Mar 08 #PHP
PHP的微信支付接口使用方法讲解
Mar 08 #PHP
PHP实现会员账号单唯一登录的方法分析
Mar 07 #PHP
PHP模糊查询技术实例分析【附源码下载】
Mar 07 #PHP
You might like
使用PHP 5.0创建图形的巧妙方法
2010/10/12 PHP
shopex主机报错误请求解决方案(No such file or directory)
2011/12/27 PHP
php 读取文件头判断文件类型的实现代码
2013/08/05 PHP
WordPress中调试缩略图的相关PHP函数使用解析
2016/01/07 PHP
PHP获取网站中各文章的第一张图片的代码示例
2016/05/20 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
从javascript语言本身谈项目实战
2006/12/27 Javascript
javascript 类型判断代码分析
2010/03/28 Javascript
Js实现当前点击a标签变色突出显示其他a标签回复原色
2013/11/27 Javascript
基于jquery实现鼠标滚轮驱动的图片切换效果
2015/10/26 Javascript
jQuery Mobile开发中日期插件Mobiscroll使用说明
2016/03/02 Javascript
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
2016/09/19 Javascript
easyui combotree加载静态数据问题(选不上)解决方法
2016/12/26 Javascript
javascript replace()第二个参数为函数时的参数用法
2016/12/26 Javascript
JS实现浏览上传文件的代码
2017/08/23 Javascript
vue加载完成后的回调函数方法
2018/09/07 Javascript
Javascript 之封装(Package)
2018/09/14 Javascript
vue debug 二种方法
2018/09/16 Javascript
Vue基础配置讲解
2019/11/29 Javascript
[22:59]VGJ.S vs VG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python中使用pprint函数进行格式化输出的教程
2015/04/07 Python
python实现list元素按关键字相加减的方法示例
2017/06/09 Python
python集合比较(交集,并集,差集)方法详解
2018/09/13 Python
Python环境Pillow( PIL )图像处理工具使用解析
2019/09/12 Python
Python代码中如何读取键盘录入的值
2020/05/27 Python
CSS3教程(4):网页边框和网页文字阴影
2009/04/02 HTML / CSS
css3 盒模型以及box-sizing属性全面了解
2016/09/20 HTML / CSS
挪威太阳镜和眼镜网上商城:SmartBuyGlasses挪威
2016/08/20 全球购物
英国领先的葡萄酒专家:Majestic Wine
2017/05/30 全球购物
美国购物网站:Clickhere2shop
2021/01/28 全球购物
学子宴答谢词
2014/01/25 职场文书
祖国在我心中演讲稿600字
2014/09/23 职场文书
2015年社区综治宣传月活动总结
2015/03/25 职场文书
​(迎国庆)作文之我爱我的祖国
2019/09/19 职场文书
pytorch 中autograd.grad()函数的用法说明
2021/05/12 Python
图神经网络GNN算法
2022/05/11 Python