加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

畅聊PHP中的多进程消费队列

发布时间:2022-07-21 13:48:07 所属栏目:PHP教程 来源:互联网
导读:引言 最近开发一个小功能,用到了队列mcq,启动一个进程消费队列数据,后边发现一个进程处理不过来了,又加了一个进程,过了段时间又处理不过来了...... 这种方式每次都要修改crontab,如果进程挂掉了,不会及时的启动,要等到下次crontab执行的时候才会启动
  引言
 
  最近开发一个小功能,用到了队列mcq,启动一个进程消费队列数据,后边发现一个进程处理不过来了,又加了一个进程,过了段时间又处理不过来了......
 
  这种方式每次都要修改crontab,如果进程挂掉了,不会及时的启动,要等到下次crontab执行的时候才会启动。关闭(重启)进程的时候用的是kill,这可能会丢失正在处理的数据,比如下面这个例子,我们假设sleep过程就是处理逻辑,这里为了明显看出效果,将处理时间放大到10s:
 
 
  <?php
 
  $i = 1;
 
  while (1) {
 
      echo "开始第[{$i}]次循环n";
 
 
  [1]    28372 terminated  php t.php
 
  nginx进程模型
 
  这时候我想到了nginx,nginx作为高性能服务器的中流砥柱,为成千上万的企业和个人服务,他的进程模型比较经典,如下所示:
 
  nginx 进程模型 图片来自网络
 
  管理员通过master进程和nginx进行交互,从/path/to/nginx.pid读取nginx master进程的pid,发送信号给master进程,master根据不同的信号做出不同的处理,然后反馈信息给管理员。worker是master进程fork出来的,master负责管理worker,不会去处理业务,worker才是具体业务的处理者,master可以控制worker的退出、启动,当worker意外退出,master会收到子进程退出的消息,也会重新启动新的worker进程补充上来,不让业务处理受影响。nginx还可以平滑退出,不丢失任何一个正在处理的数据,更新配置时nginx可以做到不影响线上服务来加载新的配置,这在请求量很大的时候特别有用。
 
  进程设计
 
  看了nginx的进模型,我们完全可以开发一个类似的类库来满足处理mcq数据的需求,做到单文件控制所有进程、可以平滑退出、可以查看子进程状态。不需要太复杂,因为我们处理队列数据接收一定的延迟,做到nginx那样不间断服务比较麻烦,费时费力,意义不是很大。设计的进程模型跟nginx类似,更像是nginx的简化版本。
  多进程模型
 
  进程信号量设计
 
  信号量是进程间通讯的一种方式,比较简单,单功能也比较弱,只能发送信号给进程,进程根据信号做出不同的处理。
 
  master进程启动的时候保存pid到文件/path/to/daeminze.pid,管理员通过信号和master进程通讯,master进程安装3种信号,碰到不同的信号,做出不同的处理,如下所示:
 
 
 
 
  SIGINT  => 平滑退出,处理完正在处理的数据再退出
 
  SIGTERM => 暴力退出,无论进程是否正在处理数据直接退出
 
  SIGUSR1 => 查看进程状态,查看进程占用内存,运行时间等信息
 
  SIGINT  => 平滑退出
 
  SIGUSR1 => 查看worker进程自身状态
 
  为什么worker进程只安装2个信号呢,少了个SIGTERM,因为master进程收到信号SIGTERM之后,向worker进程发送SIGKILL信号,默认强制关闭进程即可。
 
  worker进程是通过master进程fork出来的,这样master进程可以通过pcntl_wait来等待子进程退出事件,当有子进程退出的时候返回子进程pid,做处理并启动新的进程补充上来。
 
  master进程也通过pcntl_wait来等待接收信号,当有信号到达的时候,会返回-1,这个地方还有些坑,在下文中会详细讲。
 
  PHP中有2种信号触发的方式,第一种方式是declare(ticks = 1);,这种效率不高,Zend每执行一次低级语句,都会去检查进程中是否有未处理的信号,现在已经很少使用了,PHP 5.3.0及之前的版本可能会用到这个。
 
  第二种是通过pcntl_signal_dispatch来调用未处理的信号,PHP 5.4.0及之后的版本适用,可以巧妙的将该函数放在循环中,性能上基本没什么损失,现在推荐适用。
 
  PHP安装修信号量
 
  PHP通过pcntl_signal安装信号,函数声明如下所示:
 
 
  bool pcntl_signal ( int $signo , [callback $handler [, bool $restart_syscalls = true ] )
 
  第三个参数restart_syscalls不太好理解,找了很多资料,也没太查明白,经过试验发现,这个参数对pcntl_wait函数接收信号有影响,当设置为缺省值true的时候,发送信号,进程用pcntl_wait收不到,必须设置为false才可以,看看下面这个例子:
 
  $i = 0;
 
  while ($i<5) {
 
      $pid = pcntl_fork();
 
      $random = rand(10, 50);
 
      if ($pid == 0) {
 
  while (1) {
 
      $pid = pcntl_wait($status);
 
      var_dump($pid);
 
      pcntl_signal_dispatch();
 
  }
 
  运行之后,我们对父进程发送kill -SIGINT {$pid}信号,发现pcntl_wait没有反应,等到有子进程退出的时候,发送过的SIGINT会一个个执行,比如下面结果:
 
 
  child 29643 sleep 48
 
  child 29644 sleep 24
 
  child 29645 sleep 37
 
  child 29646 sleep 20
 
  pcntl_signal(SIGINT,  function($signo) {
 
       echo "Ctrl + Cn";
 
  }, false);
 
  这时候给父进程发送SIGINT信号,pcntl_wait会马上返回-1,信号对应的事件也会触发。
 
  所以第三个参数大概意思就是,是否重新注册此信号,如果为false只注册一次,触发之后就返回,pcntl_wait就能收到消息,如果为true,会重复注册,不会返回,pcntl_wait收不到消息。
 
  信号量和系统调用
 
  信号量会打断系统调用,让系统调用立刻返回,比如sleep,当进程正在sleep的时候,收到信号,sleep会马上返回剩余sleep秒数,比如:
 
 
 
  <?php
 
  pcntl_signal(SIGINT,  function($signo) {
 
       echo "Ctrl + Cn";
 
  }, false);
 
   
 
  while (true) {
 
      pcntl_signal_dispatch();
 
      echo "123n";
 
      $limit = sleep(2);
 
      echo "limit sleep [{$limit}] sn";
 
  }
 
  运行之后,按Ctrl + C,结果如下所示:
 
 
  123
 
  ^Climit sleep [1] s
 
  Ctrl + C
 
  123
 
  limit sleep [0] s
 
  123
 
  ^Climit sleep [1] s
 
  Ctrl + C
 
  123
 
  ^Climit sleep [2] s
 
  daemon(守护)进程
 
  这种进程一般设计为daemon进程,不受终端控制,不与终端交互,长时间运行在后台,而对于一个进程,我们可以通过下面几个步骤把他升级为一个标准的daemon进程:
 
      $pid = pcntl_fork();
 
      if (-1 == $pid) {
 
          throw new Exception("fork进程失败");
 
      } elseif ($pid != 0) {
 
          exit(0);
 
      }
 
      if (-1 == posix_setsid()) {
 
          throw new Exception("新建立session会话失败");
 
      }
 
   
 
      $pid = pcntl_fork();
 
      if (-1 == $pid) {
 
          throw new Exception("fork进程失败");
 
      } else if($pid != 0) {
 
          exit(0);
 
      }
 
   
 
      umask(0);
 
      chdir("/");
 
  }

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读