浅谈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提取中文首字母
Apr 09 PHP
php 正则 过滤html 的超链接
Jun 02 PHP
PHP中strtotime函数使用方法分享
Jan 10 PHP
PHP编码转换
Nov 05 PHP
解析PHP中数组元素升序、降序以及重新排序的函数
Jun 20 PHP
使用php判断服务器是否支持Gzip压缩功能
Sep 24 PHP
php中文验证码实现示例分享
Jan 12 PHP
php ci框架中加载css和js文件失败的解决方法
Mar 03 PHP
php使用Imagick生成图片的方法
Jul 31 PHP
简单介绍PHP的责任链编程模式
Aug 11 PHP
php lcg_value与mt_rand生成0~1随机小数的效果对比分析
Apr 05 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
Oracle 常见问题解答
2006/10/09 PHP
Codeigniter上传图片出现“You did not select a file to upload”错误解决办法
2014/06/12 PHP
PHP中file_get_contents高?用法实例
2014/09/24 PHP
php计算到指定日期还有多少天的方法
2015/04/14 PHP
[原创]ThinkPHP中SHOW_RUN_TIME不能正常显示运行时间的解决方法
2015/10/10 PHP
js 处理数组重复元素示例代码
2013/12/27 Javascript
[原创]Bootstrap 中下拉菜单修改成鼠标悬停直接显示
2016/04/14 Javascript
BootStrap中Table分页插件使用详解
2016/10/09 Javascript
jQuery和JavaScript节点插入元素的方法对比
2016/11/18 Javascript
bootstrap paginator分页前后台用法示例
2017/06/17 Javascript
React中上传图片到七牛的示例代码
2017/10/10 Javascript
Angular4.0中引入laydate.js日期插件的方法教程
2017/12/25 Javascript
Webpack path与publicPath的区别详解
2018/05/03 Javascript
Javascript 关于基本类型和引用类型的个人理解
2019/11/01 Javascript
Python内置的字符串处理函数整理
2013/01/29 Python
用C++封装MySQL的API的教程
2015/05/06 Python
轻松理解Python 中的 descriptor
2017/09/15 Python
使用k8s部署Django项目的方法步骤
2019/01/14 Python
python实现手机销售管理系统
2019/03/19 Python
使用python搭建服务器并实现Android端与之通信的方法
2019/06/28 Python
Django的用户模块与权限系统的示例代码
2019/07/24 Python
django 单表操作实例详解
2019/07/30 Python
如何用Python来搭建一个简单的推荐系统
2019/08/07 Python
Python random模块制作简易的四位数验证码
2020/02/01 Python
详解Python中第三方库Faker
2020/09/25 Python
用HTML5制作数字时钟的教程
2015/05/11 HTML / CSS
用HTML5制作烟火效果的教程
2015/05/12 HTML / CSS
JPA的特点
2014/10/25 面试题
servlet面试题
2012/08/20 面试题
个人自我剖析材料
2014/02/07 职场文书
大学毕业生个人自荐书
2014/07/02 职场文书
2014年预备党员群众路线教育实践活动对照检查材料思想汇报
2014/10/02 职场文书
有限责任公司股东合作协议书范本
2014/10/30 职场文书
英语教师求职信范文
2015/03/20 职场文书
幼儿园门卫安全责任书
2015/05/08 职场文书
python_tkinter事件类型详情
2022/03/20 Python