浅谈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 SQL防注入代码集合
Apr 25 PHP
PHP删除特定数组内容并且重建数组索引的方法.
Mar 25 PHP
适用于php-5.2 的 php.ini 中文版[金步国翻译]
Apr 17 PHP
php mssql扩展SQL查询中文字段名解决方法
Oct 15 PHP
PHP数据集构建JSON格式及新数组的方法
Nov 07 PHP
php导出word文档与excel电子表格的简单示例代码
Mar 08 PHP
php实现水仙花数的4个示例分享
Apr 08 PHP
php根据日期显示所在星座的方法
Jul 13 PHP
3种方法轻松处理php开发中emoji表情的问题
Jul 18 PHP
PHP检查网站是否宕机的方法示例
Jul 24 PHP
PHP结合Ffmpeg快速搭建流媒体服务的实践记录
Oct 31 PHP
PHP类与对象后期静态绑定操作实例详解
Dec 20 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中的cookie
2006/11/26 PHP
PHP+MySQL实现无极限分类栏目的方法
2015/12/23 PHP
最近项目写了一些js,水平有待提高
2009/01/31 Javascript
js 纯数字不重复排列的另类方法
2010/07/17 Javascript
分享一个用Mootools写的鼠标滑过进度条改变进度值的实现代码
2011/12/12 Javascript
使用javascipt---实现二分查找法
2013/04/10 Javascript
javascript获得网页窗口实际大小的示例代码
2013/09/21 Javascript
jQuery实现首页顶部可伸缩广告特效代码
2015/04/15 Javascript
判断输入的字符串是否是日期格式的简单方法
2016/07/11 Javascript
浅谈angularjs module返回对象的坑(推荐)
2016/10/21 Javascript
概述一个页面从输入URL到页面加载完的过程
2016/12/16 Javascript
JavaScript仿支付宝6位数字密码输入框
2016/12/29 Javascript
checkbox:click事件触发span元素内容改变的方法
2017/09/11 Javascript
如何选择适合你的JavaScript框架
2017/11/20 Javascript
nodejs实现的简单web服务器功能示例
2018/03/15 NodeJs
vue2.0 中使用transition实现动画效果使用心得
2018/08/13 Javascript
Vue实现日历小插件
2019/06/26 Javascript
基于vue实现圆形菜单栏组件
2019/07/05 Javascript
原生js实现二级联动菜单
2019/11/27 Javascript
[01:57]2018年度DOTA2最具潜力解说-完美盛典
2018/12/16 DOTA
Python实现获取前100组勾股数的方法示例
2018/05/04 Python
Python读取excel中的图片完美解决方法
2018/07/27 Python
django中的图片验证码功能
2019/09/18 Python
python 三种方法实现对Excel表格的读写
2020/11/19 Python
CSS3新增布局之: flex详解
2020/06/18 HTML / CSS
浅析CSS3 用text-overflow解决文字排版问题
2020/10/28 HTML / CSS
简历中个人自我评价范文
2013/12/26 职场文书
学习自我鉴定
2014/02/01 职场文书
结婚保证书范文
2014/04/29 职场文书
政府绩效管理实施方案
2014/05/04 职场文书
解除劳动关系协议书范文
2014/09/11 职场文书
2014年护士工作总结范文
2014/11/11 职场文书
2014年环境卫生工作总结
2014/11/24 职场文书
毕业答辩开场白范文
2015/05/27 职场文书
《世界多美呀》教学反思
2016/02/22 职场文书
MySQL七大JOIN的具体使用
2022/02/28 MySQL