浅谈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 获取远程网页内容的函数
Sep 08 PHP
浅析php fwrite写入txt文件的时候用 \r\n不能换行的问题
Aug 06 PHP
php环境下利用session防止页面重复刷新的具体实现
Jan 09 PHP
php判断手机访问还是电脑访问示例分享
Jan 20 PHP
19个超实用的PHP代码片段
Mar 14 PHP
php的mssql数据库连接类实例
Nov 28 PHP
php分页查询mysql结果的base64处理方法示例
May 18 PHP
PHP排序二叉树基本功能实现方法示例
May 26 PHP
PHP实现正则匹配所有括号中的内容
Jun 22 PHP
PHP获取星期几的常用方法小结
Dec 18 PHP
定位地理位置PHP判断员工打卡签到经纬度是否在打卡之内
May 23 PHP
TP5框架实现上传多张图片的方法分析
Mar 29 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
世界上第一台立体声收音机
2021/03/01 无线电
Windows Apache2.2.11及Php5.2.9-1的安装与配置方法
2009/06/08 PHP
ThinkPHP空模块和空操作详解
2014/06/30 PHP
PHP分享图片的生成方法
2018/04/25 PHP
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
js无刷新操作table的行和列
2014/03/27 Javascript
nodejs加密Crypto的实例代码
2016/07/07 NodeJs
js数组去重的hash方法
2016/12/22 Javascript
validationEngine 表单验证插件使用实例代码
2017/06/15 Javascript
Js中将Long转换成日期格式的实现方法
2018/06/05 Javascript
jQuery超简单遮罩层实现方法示例
2018/09/06 jQuery
详解vue在项目中使用百度地图
2019/03/26 Javascript
Vue+Koa2 打包后进行线上部署的教程详解
2019/07/31 Javascript
jquery实现购物车基本功能
2019/10/25 jQuery
Python文件右键找不到IDLE打开项解决办法
2015/06/08 Python
浅谈Python数据类型判断及列表脚本操作
2016/11/04 Python
Python Nose框架编写测试用例方法
2017/10/26 Python
python实现换位加密算法的示例
2018/10/14 Python
python 拼接文件路径的方法
2018/10/23 Python
基于wxPython的GUI实现输入对话框(1)
2019/02/27 Python
深度辨析Python的eval()与exec()的方法
2019/03/26 Python
Python:Numpy 求平均向量的实例
2019/06/29 Python
Django中如何使用sass的方法步骤
2019/07/09 Python
python类的实例化问题解决
2019/08/31 Python
Django框架 查询Extra功能实现解析
2019/09/04 Python
python 字典有序并写入json文件过程解析
2019/09/30 Python
python3连接kafka模块pykafka生产者简单封装代码
2019/12/23 Python
python:解析requests返回的response(json格式)说明
2020/04/30 Python
会计专业大学生职业生涯规划范文
2014/01/11 职场文书
领导班子整改方案和个人整改措施
2014/10/25 职场文书
2014幼儿园卫生保健工作总结
2014/12/05 职场文书
布达拉宫导游词
2015/02/02 职场文书
医院保洁员岗位职责
2015/02/13 职场文书
餐厅保洁员岗位职责
2015/04/10 职场文书
Springboot如何使用logback实现多环境配置?
2021/06/16 Java/Android
Win11开始菜单添加休眠选项
2022/04/19 数码科技