浅谈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中文件上传的安全问题
Oct 09 PHP
PHP 和 XML: 使用expat函数(三)
Oct 09 PHP
PHP 函数执行效率的小比较
Oct 17 PHP
php Calender(日历)代码分享
Jan 03 PHP
php的sprintf函数的用法 控制浮点数格式
Feb 14 PHP
浅析PHP微信支付通知的处理方式
May 25 PHP
PHP mkdir()无写权限的问题解决方法
Jun 19 PHP
php集成环境xampp中apache无法启动问题解决方案
Nov 18 PHP
PHP实现伪静态方法汇总
Jan 13 PHP
ThinkPHP框架搭建及常见问题(XAMPP安装失败、Apache/MySQL启动失败)
Apr 15 PHP
CodeIgniter框架基本增删改查操作示例
Mar 23 PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
Mar 30 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中鲜为人知的10个函数
2014/02/28 PHP
详解PHP序列化反序列化的方法
2015/10/27 PHP
PHP中FTP相关函数小结
2016/07/15 PHP
php array_multisort 对数组进行排序详解及实例代码
2016/10/27 PHP
php上传excel表格并获取数据
2017/04/27 PHP
PC端微信扫码支付成功之后自动跳转php版代码
2017/07/07 PHP
Javascript实例教程(19) 使用HoTMetal(6)
2006/12/23 Javascript
使用TextRange获取输入框中光标的位置的代码
2007/03/08 Javascript
Node.js中对通用模块的封装方法
2014/06/06 Javascript
js实现按钮颜色渐变动画效果
2015/08/20 Javascript
学习使用jquery iScroll.js移动端滚动条插件
2020/03/24 Javascript
JS如何设置cookie有效期为当天24点并弹出欢迎登陆界面
2016/08/04 Javascript
前端js实现文件的断点续传 后端PHP文件接收
2016/10/14 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
Vue.js手风琴菜单组件开发实例
2017/05/16 Javascript
js使用xml数据载体实现城市省份二级联动效果
2017/11/08 Javascript
Elasticsearch实现复合查询高亮结果功能
2019/09/10 Javascript
layui table 获取分页 limit的方法
2019/09/20 Javascript
javascript sort()对数组中的元素进行排序详解
2019/10/13 Javascript
python中map、any、all函数用法分析
2015/04/21 Python
理解python正则表达式
2016/01/15 Python
Python 正则表达式入门(初级篇)
2016/12/07 Python
Python运算符重载详解及实例代码
2017/03/07 Python
VSCode下好用的Python插件及配置
2018/04/06 Python
详解Python odoo中嵌入html简单的分页功能
2019/05/29 Python
Coggles美国/加拿大:高级国际时装零售商
2018/10/23 全球购物
美国木工工具和用品商店:Woodcraft
2019/10/30 全球购物
如何利用XMLHTTP检测URL及探测服务器信息
2013/11/10 面试题
财务会计自荐信范文
2014/02/21 职场文书
基层党建工作宣传标语
2014/06/24 职场文书
个人四风对照检查材料
2014/09/26 职场文书
2014年心理健康教育工作总结
2014/12/06 职场文书
小学教学工作总结2015
2015/05/13 职场文书
大学迎新生欢迎词
2015/09/29 职场文书
Django 实现jwt认证的示例
2021/04/30 Python
JavaScript前端面试扁平数据转tree与tree数据扁平化
2022/06/14 Javascript