博客链接:http://www.cnblogs.com/zhenghongxin/p/8676565.html
消息队列处理后台任务带来的问题
在系统稍微大些的时候,我们经常会用到消息队列(实现的方式很多种,在后续会讲到),比如发邮件,将邮件消息放入队列待执行,比如发送奖励等。可以说,我们是构造一个最简单的先进先出队列,队列的一个成员就是一段文本。入消息队列lpop比较容易,但在取消息队列rpop的时候,我们可能遇到这样的问题:
我们无法预知消息队列何时会有数据产生,所以我们的任务执行程序还需要具备监控消息队列的能力,也就是一个常驻后台的守护进程。
而PHP在一般情况下是无法常驻内存的,当然有了swoole后,PHP可以常驻内存(可以学习:MixPHP:基于 Swoole 的常驻内存型 PHP 框架),或其他方式来实现常驻内存(workman等),而接下来面对的问题是:
1)当守护进程运行时,Web应用能否与后台守护进程交互,实现开启/杀死进程的功能以及获得进程的运行状态?
2)当我们更改配置时,守护进程能否实时读取配置而不需要整个重启?
3)守护进程如果重启,能否进行平滑的重启,而不会丢失正在执行的消息队列?
4)消息队列的如何取出与意外还原保证消息的完整性?
简单的php定时任务拉起消费队列
我们可以写一个php消费队列的脚本,利用定时任务,每隔几分钟读取一下消息队列,执行成功的队列清除,每隔五分钟拉起任务。这样子做的优点:代码简单易理解,定时任务容易配置
这样子做的缺点:
1) 最慢情况下,消息队列要等5分钟才会被消费,在某些场景下,这是无法忍受的。
2) 消息队列很多的情况下,每隔五分钟,任务被重复拉起,要考虑多种并发,堵塞的情况。当出了问题,如何回滚?突然间进程异常退出,队列没有被消费等情况
后台进程与定时任务双配合
我们可以写一个php消费队列的脚本,让其以后台进程的方式运行,一直在后台默默运行。不断地等待消息队列的产生,为了保证这个进程发生意外断掉,我们需要一个守护进程来守护这个后台进程。
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。顾名思义,它的作用用来守护我们的消息队列消费进程,它将后台程序变成一种服务,让服务一直处于运行状态。当后台程序被kill掉,守护进程会将它重新拉起。
1)守护进程最重要的特性是后台运行。
2)守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。
3)守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(shell)执行。
简单的实现来说,用定时任务cron,每五分钟检测一下后台进程是否正常运行,如果正常运行,那不作处理,如果后
台进程挂掉,那将它重新拉起。
这是简单的用cron来做守护任务的例子,很多人也提出了让php做守护进程的方法:
1)将文件模式的屏蔽字设置为0;
2)fork出子进程,终止父进程(保证创建守护进程的进程不是进程组的组长);
3)调用setsid创建一个新的会话;
4)将当前工作目录改为根目录;
5)关闭不需要的文件描述符;
6)忽略SIGCHLD信号。
具体可以看:
如何写php守护进程(Daemon)http://www.jb51.net/article/77205.htm
此方法步骤繁琐麻烦,且不易维护,并不推荐这么做
swoole来做守护进程
有朋友的公司用的是swoole来做守护进程,这方面刚开始我们有考虑,最后决定用更加简单的Supervisor来做
Supervisor
Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动,这样只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去即可。也实现当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,可以选择是否自己启动和报警。supervisor还提供了一个功能,可以为supervisord或者每个子进程,设置一个非root的user,这个user就可以管理它对应的进程。
安装
Supervisor是使用python开发的一个进程管工具,安装和使用都非常简单。使用 pip 或者 easy_install 安装会非常方便,自动解决依赖关系。
– sudo pip install supervisor
– sudo easy_install supervisor
supervisor安装完成后会生成三个执行程序:supervisortd、supervisorctl、echo_supervisord_conf,分别是supervisor的守护进程服务(用于接收进程管理命令)、客户端(用于和守护进程通信,发送管理进程的指令)、生成初始配置文件程序。
新建个保存配置的目录:
mkdir /etc/supervisor
用生成配置的命令生成配置查看:
echo_supervisord_conf > /etc/supervisor/supervisord.conf
可以查看 /etc/supervisor/supervisord.conf 下的配置文件
某些配置参数说明:
[unix_http_server]
file=/tmp/supervisor.sock ;UNIX socket 文件,supervisorctl 会使用
;chmod=0700 ;socket文件的mode,默认是0700
;chown=nobody:nogroup ;socket文件的owner,格式:uid:gid;[inet_http_server] ;HTTP服务器,提供web管理界面
;port=127.0.0.1:9001 ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
;username=user ;登录管理后台的用户名
;password=123 ;登录管理后台的密码[supervisord]
logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
logfile_backups=10 ;日志文件保留备份数量默认10,设为0表示不备份
loglevel=info ;日志级别,默认info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false ;是否在前台启动,默认是false,即以 daemon 的方式启动
minfds=1024 ;可以打开的文件描述符的最小值,默认 1024
minprocs=200 ;可以打开的进程数的最小值,默认 200[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord; [program:xx]是被管理的进程配置参数,xx是进程的名称
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run ; 程序启动命令
autostart=true ; 在supervisord启动的时候也自动启动
startsecs=10 ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
startretries=3 ; 启动失败自动重试次数,默认是3
user=tomcat ; 用哪个用户启动进程,默认是root
priority=999 ; 进程启动优先级,默认999,值小的优先启动
redirect_stderr=true ; 把stderr重定向到stdout,默认false
stdout_logfile_maxbytes=20MB ; stdout 日志文件大小,默认50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数,默认是10
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
stopasgroup=false ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false ;默认为false,向进程组发送kill信号,包括子进程;包含其它配置文件
[include]
files = relative/directory/*.ini ;可以指定一个或多个以.ini结束的配置文件
更为具体的配置,我们可以查看:
https://blog.csdn.net/xyang81/article/details/51555473
上面有更为详细的开机启动,web管理等各项说明,在此我不列举了
我本地的简略配置为:
command=php /phpstudy/www/index.php
stdout_logfile=/tmp/catalina.out
autostart=true
user=root
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
启动后,可以尝试kill掉index.php进程,几秒内进程都会重新被拉起
*** 值得注意的是:如果你在代码中,刻意的去退出进程,那supervisor依旧会帮你重新拉起。