我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PHP中的多进程消费队列有什么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PHP中的多进程消费队列有什么用

小编给大家分享一下PHP中的多进程消费队列有什么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

最近开发一个小功能,用到了队列mcq,启动一个进程消费队列数据,后边发现一个进程处理不过来了,又加了一个进程,过了段时间又处理不过来了......

这种方式每次都要修改crontab,如果进程挂掉了,不会及时的启动,要等到下次crontab执行的时候才会启动。关闭(重启)进程的时候用的是kill,这可能会丢失正在处理的数据,比如下面这个例子,我们假设sleep过程就是处理逻辑,这里为了明显看出效果,将处理时间放大到10s:

<?php$i = 1;while (1) {    echo "开始第[{$i}]次循环\n";    sleep(10);    echo "结束第[{$i}]次循环\n";    $i++;}

当我们运行脚本之后,等到循环开始之后,给进程发送 kill {$pid},默认发送的是编号为15的SIGTERM信号。假设$i是从队列拿到的,拿到2的时候,正在处理,我们给程序发送了kill信号,和队列数据丢失一样,问题比较大,因此我要想办法解决这些问题。

开始第[1]次循环结束第[1]次循环开始第[2]次循环[1]    28372 terminated  php t.php

nginx进程模型

这时候我想到了nginx,nginx作为高性能服务器的中流砥柱,为成千上万的企业和个人服务,他的进程模型比较经典,如下所示:

PHP中的多进程消费队列有什么用

管理员通过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的简化版本。
PHP中的多进程消费队列有什么用

进程信号量设计

信号量是进程间通讯的一种方式,比较简单,单功能也比较弱,只能发送信号给进程,进程根据信号做出不同的处理。

master进程启动的时候保存pid到文件/path/to/daeminze.pid,管理员通过信号和master进程通讯,master进程安装3种信号,碰到不同的信号,做出不同的处理,如下所示:

SIGINT => 平滑退出,处理完正在处理的数据再退出SIGTERM => 暴力退出,无论进程是否正在处理数据直接退出SIGUSR1 => 查看进程状态,查看进程占用内存,运行时间等信息

master进程通过信号和worker进程通讯,worker进程安装了2个信号,如下所示:

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才可以,看看下面这个例子:

<?php$i = 0;while ($i<5) {    $pid = pcntl_fork();    $random = rand(10, 50);    if ($pid == 0) {        sleep($random);        exit();    }    echo "child {$pid} sleep {$random}\n";    $i++;}pcntl_signal(SIGINT,  function($signo) {     echo "Ctrl + C\n";});while (1) {    $pid = pcntl_wait($status);    var_dump($pid);    pcntl_signal_dispatch();}

运行之后,我们对父进程发送kill -SIGINT {$pid}信号,发现pcntl_wait没有反应,等到有子进程退出的时候,发送过的SIGINT会一个个执行,比如下面结果:

child 29643 sleep 48child 29644 sleep 24child 29645 sleep 37child 29646 sleep 20child 29647 sleep 31int(29643)Ctrl + CCtrl + CCtrl + CCtrl + Cint(29646)

这是运行脚本之后马上给父进程发送了四次SIGINT信号,等到一个子进程推出的时候,所有信号都会触发。

但当把安装信号的第三个参数设置为false

pcntl_signal(SIGINT,  function($signo) {     echo "Ctrl + C\n";}, false);

这时候给父进程发送SIGINT信号,pcntl_wait会马上返回-1,信号对应的事件也会触发。

所以第三个参数大概意思就是,是否重新注册此信号,如果为false只注册一次,触发之后就返回,pcntl_wait就能收到消息,如果为true,会重复注册,不会返回,pcntl_wait收不到消息。

信号量和系统调用

信号量会打断系统调用,让系统调用立刻返回,比如sleep,当进程正在sleep的时候,收到信号,sleep会马上返回剩余sleep秒数,比如:

<?phppcntl_signal(SIGINT,  function($signo) {     echo "Ctrl + C\n";}, false);while (true) {pcntl_signal_dispatch();    echo "123\n";    $limit = sleep(2);echo "limit sleep [{$limit}] s\n";}

运行之后,按Ctrl + C,结果如下所示:

123^Climit sleep [1] sCtrl + C123limit sleep [0] s123^Climit sleep [1] sCtrl + C123^Climit sleep [2] s
daemon(守护)进程

这种进程一般设计为daemon进程,不受终端控制,不与终端交互,长时间运行在后台,而对于一个进程,我们可以通过下面几个步骤把他升级为一个标准的daemon进程:

protected function daemonize(){    $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("/");}

拢共分五步:

  1. fork子进程,父进程退出。

  2. 设置子进程为会话组长,进程组长。

  3. 再次fork,父进程退出,子进程继续运行。

  4. 恢复文件掩码为0

  5. 切换当前目录到根目录/

第2步是为第1步做准备,设置进程为会话组长,必要条件是进程非进程组长,因此做第一次fork,进程组长(父进程)退出,子进程通过posix_setsid()设置为会话组长,同时也为进程组长。

第3步是为了不让进程重新控制终端,因为一个进程控制一个终端的必要条件是会话组长(pid=sid)。

第4步是为了恢复默认的文件掩码,避免之前做的操作对文件掩码做了设置,带来不必要的麻烦。关于文件掩码, linux中,文件掩码在创建文件、文件夹的时候会用到,文件的默认权限为666,文件夹为777,创建文件(夹)的时候会用默认值减去掩码的值作为创建文件(夹)的最终值,比如掩码022下创建文件666 - 222 = 644,创建文件夹777 - 022 = 755

掩码新建文件权限新建文件夹权限
umask(0)666 (-rw-rw-rw-)777 (drwxrwxrwx)
umask(022)644 (-rw-r--r--)755 (drwxr-xr-x)

第5步是切换了当前目录到根目录/,网上说避免起始运行他的目录不能被正确卸载,这个不是太了解。

对应5步,每一步的各种id变化信息:

操作后pidppidpgidsid
开始17723313811772331381
第一次fork1772311772331381
posix_setsid()1774011774017740
第二次fork1784011774017740

另外,会话、进程组、进程的关系如下图所示,这张图有助于更好的理解。
PHP中的多进程消费队列有什么用

至此,你也可以轻松地造出一个daemon进程了。

命令设计

我准备给这个类库设计6个命令,如下所示:

  1. start 启动命令

  2. restart 强制重启

  3. stop 平滑停止

  4. reload 平滑重启

  5. quit 强制停止

  6. status 查看进程状态

启动命令

启动命令就是默认的流程,按照默认流程走就是启动命令,启动命令会检测pid文件中是否已经有pid,pid对应的进程是否健康,是否需要重新启动。

强制停止命令

管理员通过入口文件结合pid给master进程发送SIGTERM信号,master进程给所有子进程发送SIGKILL信号,等待所有worker进程退出后,master进程也退出。

强制重启命令

强制停止命令 + 启动命令

平滑停止命令

平滑停止命令,管理员给master进程发送SIGINT信号,master进程给所有子进程发送SIGINT,worker进程将自身状态标记为stoping,当worker进程下次循环的时候会根据stoping决定停止,不在接收新的数据,等所有worker进程退出之后,master进程也退出。

平滑重启命令

平滑停止命令 + 启动命令

查看进程状态

查看进程状态这个借鉴了workerman的思路,管理员给master进程发送SIGUSR1信号,告诉主进程,我要看所有进程的信息,master进程,master进程将自身的进程信息写入配置好的文件路径A中,然后发送SIGUSR1,告诉worker进程把自己的信息也写入文件A中,由于这个过程是异步的,不知道worker进程啥时候写完,所以master进程在此处等待,等所有worker进程都写入文件之后,格式化所有的信息输出,最后输出的内容如下所示:

➜/dir /usr/local/bin/php DaemonMcn.php statusDaemon [DaemonMcn] 信息:-------------------------------- master进程状态 --------------------------------pid       占用内存       处理次数       开始时间                 运行时间16343     0.75M          --             2018-05-15 09:42:45      0 天 0 时 3 分12 slaver-------------------------------- slaver进程状态 --------------------------------任务task-mcq:16345     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分16346     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分--------------------------------------------------------------------------------任务test-mcq:16348     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分16350     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分16358     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分16449     0.75M          1              2018-05-15 09:46:40      0 天 0 时 0 分--------------------------------------------------------------------------------

等待worker进程将进程信息写入文件的时候,这个地方用了个比较trick的方法,每个worker进程输出一行信息,统计文件的行数,达到worker进程的行数之后表示所有worker进程都将信息写入完毕,否则,每个1s检测一次。

其他设计

另外还加了两个比较实用的功能,一个是worker进程运行时间限制,一个是worker进程循环处理次数限制,防止长时间循环进程出现内存溢出等意外情况。时间默认是1小时,运行次数默认是10w次。

除此之外,也可以支持多任务,每个任务几个进程独立开,统一由master进程管理。

代码已经放到github中,有兴趣的可以试试,不支持windows哦,有什么错误还望指出来。

看完了这篇文章,相信你对“PHP中的多进程消费队列有什么用”有了一定的了解,如果想了解更多相关知识,欢迎关注编程网行业资讯频道,感谢各位的阅读!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PHP中的多进程消费队列有什么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP中的多进程消费队列有什么用

小编给大家分享一下PHP中的多进程消费队列有什么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!最近开发一个小功能,用到了队列mcq,启动一个进程消费队列数据,后边发现一个进程处理不过来了,又加了一个进程,过了段时间又处理
2023-06-06

laravel队列并发消费的作用是什么

Laravel队列并发消费的作用是提高任务处理的效率和速度。通过并发消费,可以同时处理多个队列任务,而不是顺序地一个一个处理。这样可以节省时间,并且能够更快地处理大量的任务。并发消费还可以提高系统的负载均衡能力。当系统面临高并发请求时,通过
2023-09-06

java中的消息队列怎么利用多线程实现

java中的消息队列怎么利用多线程实现?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、定义一个队列缓存池: //static修饰的成员变量和成员方法独立于该类的任何对象。也就
2023-05-31

Java中消息队列的作用是什么

这篇文章主要讲解了“Java中消息队列的作用是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中消息队列的作用是什么”吧!1、 这些接口之间耦合比较严重,每新增一个下游功能,都要
2023-06-16

SpringBoot中的消息队列怎么利用redis进行集成

SpringBoot中的消息队列怎么利用redis进行集成?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、pom文件依赖   
2023-05-31

PHP消息队列实现及运用的方法是什么

这篇文章主要讲解了“PHP消息队列实现及运用的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP消息队列实现及运用的方法是什么”吧!消息队列的概念、原理、实现方式概念队列结构的一
2023-07-04

java多线程队列的使用方法是什么

在Java中,可以使用BlockingQueue来实现多线程队列。BlockingQueue是一个线程安全的队列,它提供了put()和take()方法来实现元素的插入和获取。以下是使用BlockingQueue的示例代码:首先,创建一个B
2023-10-24

CRM中间件里的CSA队列有什么作用

本篇内容主要讲解“CRM中间件里的CSA队列有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“CRM中间件里的CSA队列有什么作用”吧!我们有时候会在中间件的事务码SMQ2即Inbound
2023-06-04

python多进程中的生产者和消费者模型怎么实现

这篇文章主要介绍了python多进程中的生产者和消费者模型怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇python多进程中的生产者和消费者模型怎么实现文章都会有所收获,下面我们一起来看看吧。Pytho
2023-07-05

Java多线程中消费者与生产者的关系是什么

这篇文章将为大家详细讲解有关Java多线程中消费者与生产者的关系是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。  多线程:CPU中各种任务在交替执行过程中,被称为多线程处理。其中,每个任务的一次动态
2023-06-02

Java多线程编程中的锁有什么用

这篇文章主要讲解了“Java多线程编程中的锁有什么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程编程中的锁有什么用”吧!阅读目录一、尽量不要锁住方法二、缩小同步代码块,只锁数
2023-06-17

Linux 进程管理中的CFS负载均衡有什么用

本篇内容介绍了“Linux 进程管理中的CFS负载均衡有什么用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 什么是负载均衡?前面的调度学习
2023-06-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录