我们在php-fpm 模式下编写的代码一般是同步阻塞的,很少能实现像redis 或者 node 那种等待监听固定端口的形式(通过daemon 或者 nohup参数让其在后台执行),一般php 想实现同样的方式,都是通过加一个死循环,while (true), 但这种方式有一个缺点就是程序挂掉了不能重写启动,不像php-fpm 或者 swoole 的http server 在对子进程进行管理的时候,当工作进程挂掉之后,会重新拉起一个新进程,怎么办呢?可以通过两个工具 pm2 或者 supervisor 。supervisor 的工作原理就是把管理的进程当做自己的子进程,pm2 原理没有了解过。
举个例子吧,之前接手的一个任务毒和nike关联出价,当前台下发撤单任务的时候,因为牵扯到很多curl 请求,不能同步实现,1.感觉可以通过把请求内容放到mq当中,然后利用daemon 化的task始终监听这个mq,如果有内容,则触发任务,往后执行,否则停歇1min 继续监听。2.那个前作者是通过把内容放到redis中,通过daemon化的task监听这个redis,如果检测到这个redis这个key,就执行相应任务。
总结一下吧,其实上面两个方式php的daemon话都是通过while true,那么supervisor 的作用是啥呢,当php的这个脚本挂掉了,supervisor可以重新拉起这个脚本。同理pm2也可以,而且不一定是脚本挂掉,脚本执行完了,也可以重新拉起,比如
1 | <?php |
上面的例子还有个很重要的技术点就是定时触发。相比较crontab 的task,上面的task 触发任务时间不一致,触发时间靠前台控制,所以我们这时候就通过始终运行一个后台任务,然后靠前台触发一个开关,后端始终运行的task始终监测这个开关,如果是开就运行,虽然可能存在一定时间的误差,但大体上还是能完成任务的。
再比如之前接手的一个安卓灰度更新的任务,当某个渠道开启灰度之后,我们需要每隔30min发送一条钉钉消息,我们同样可以检测表中渠道是否有开启灰度的状态,如果有,则发一条消息,并记录通知时间,下次的循环任务检测是否灰度还在开启,如果还在开启,检测时间通知是否超过30min,如果超过了,再发送消息,并更新通知时间。
上面的任务看似已经解决,但还是会涉及一个多进程问题,再举个例子,比如erp系统,需要爬取商户的店铺信息,这个task 要一直执行,如果我们有100个商户,就得运行100个task,启动100个脚本。我们上面灰度通知的好解决,一种办法就是检测运行的渠道,循环运行的渠道列表,然后发送消息通知,另一种就是一次性检测,把渠道更新信息包含在一条短消息中发出去,之所以这么做是因为后续的任务(发送钉钉通知)可以在很短时间内完成,如果像erp的那种爬虫任务,只能通过启动多个脚本了。
关于php的多进程扩展pnctl 这里暂时不做讨论,说一下pm2 和 supervisor 的简单使用。
PM2
我比较常用的命令
1 | pm2 list |
pm2 配置简单,非常好用,echo 的内容之间在log中能看到,除了node, php也是能管理的
(PM2 的安装需要通过node 和 npm, node 的安装很简单,但我们一定要注意npm 安装的module 位置,如果是linux 下,一定要配置好node_path ,否则 npm -g 安装的内容可能不能使用, npm config get prefix 可以查看node -g 包安装的位置 。同理node的安装位置最好也是/usr/local/ 下面,方便直接访问)
今天在使用pm2 管理我的一个task的时候遇到一个问题,就是普通的脚本pm2 start app.js 或者 pm2 start test.php, 但是我现在的命令是这样的
1 | php symfony trade:UpdateCommentDgTags updateUserPublishment --split=4 |
之前的那个命令是肯定不可以的,需要使用配置文件
1 | { //统计报表更新 |
一定要注意,这个地方symonfy 是脚本,怪不得之前我在执行task 命令的时候忘记输入symfony ,他说找不到脚本。 后面的那一长串都会统一看成参数,只是参数解析的形式不一样,比如 trade:UpdateCommentDgTags, 解析成方法名称,–split = 4 解析成参数。
1 | ps: php test.php --goods_id=12 uo22 |
pm2 log日志的位置
1 | error log path │ /home/username/.pm2/logs/app-error-0.log |
需要注意的是这个error 应该是pm2 自身的error, 一般我们程序抛出的都是在out 日志里面
!!~notice: pm2 虽然香,但也不能乱用,比如之前我在预发布机器上跑的一个task 脚本,因为跑完了,脚本 die, 不断的被pm2重启,导致cpu 很快被耗尽,所以很多脚本停止的时候 都会有个sleep 操作,过一段时间再重启。当然如果你是那种常驻内存,比如node 做的webserver 肯定不用,因为一般不会崩溃,唯一一种就是你的代码有问题,不断的报错,导致服务不断的被唤醒。
supervisor
配置文件里面一定要有[supervisord]和[supervisorctl] 部分,否则会报错。这篇文章应该还可以 需要注意的就是监听端口从 127.0.0.1:9001 改成 0.0.0.0:9001 ,方便外网访问,贴个图
基本就能用啦 (比如我那几百万的任务task就能通过这两个处理,不用我一直看着了,跑完了看看 log就好了)