介绍
Supervisor 是一个由 Python 实现,基于 client/server 模型的系统,用于监控和管理进程。目前,只支持在 *inx 系统下运行。
它主要由四个组件组成,分别是:
- supervisord(server 端)
- supervisorctl(client 端)
- XML-RPC API (client 端)
- Web UI(client 端)
使用场景
- 保证服务的可用(autostart=true,autorestart=unexpected)
- 多进程(numprocs=num,num>1)
- 后台进程(没使用 supervisord 之前,一般通过 nohup command & 实现)
安装
这里使用 pip 安装 supervisor。没有安装 pip 的话,使用下面的步骤安装。已有请忽略。
# 安装 pip
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python get-pip.py
# 使用 pip 安装 supervisor
$ sudo pip install supervisor
使用
配置及启动 supervisord
# 创建需要的目录结构
$ sudo mkdir -pv /etc/supervisor.d/conf.d
# 生成默认的配置文件
$ sudo echo_supervisord_conf > /etc/supervisor.d/supervisord.conf
# 添加示例配置
$ sudo cat > /etc/supervisor.d/conf.d/example.conf << "EOF"[program:example]
process_name=%(program_name)s_%(process_num)02d
command=/path/to/command
autostart=true
autorestart=true
user=www
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/supervisor/example.log
EOF
# 启动 supervisord
$ sudo supervisord -c /etc/supervisor.d/supervisord.conf
启动 supervisord 之后,会产生如下的进程:
$ pstree -auns `pgrep supervisor`
systemd
└─/usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf
├─/path/to/command
├─/path/to/command
├─/path/to/command
└─/path/to/command
Supervisorctl 的简单使用
supervisorctl -c /etc/supervisord.conf
上面这个命令会进入 supervisorctl 的 shell 界面,然后可以执行不同的命令了:
# 查看程序状态
> status
# 关闭 usercenter 程序
> stop usercenter
# 启动 usercenter 程序
> start usercenter
# 重启 usercenter 程序
> restart usercenter
# 读取有更新(增加)的配置文件,不会启动新添加的程序
> reread
# 重启配置文件修改过的程序
> update
事件
Supervisord 示例配置
[eventlistener:event_listener]
command=php /path/to/examples/log.php
process_name=%(program_name)s_%(process_num)02d
numprocs=1
events=PROCESS_STATE_STARTING,TICK_5
autostart=true
autorestart=unexpected
事件处理脚本
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Mtdowling\Supervisor\EventListener;
use Mtdowling\Supervisor\EventNotification;
$listener = new EventListener();
$listener->listen(function(EventListener $listener, EventNotification $event) {
$listener->log($event->getEventName());
$listener->log($event->getServer());
$listener->log($event->getPool());
// Try messing around with supervisorctl to restart processes and see what
// data is available
$listener->log(var_export($event->getData(), true));
return true;
});
注意问题
1. 子进程不能是守护进程
对应错误: Exited too quickly
子进程在退出时,Supervisord
会收到 SIGCHLD
信号,接着去执行相应的操作。
而守护进程一般会在 fork 之后,exit 掉父进程。
2. 内存泄漏
- valgrind
- max_requests
附录
Handle SIGCHLD in C
// Via http://www.linuxquestions.org/questions/programming-9/how-a-father-process-know-which-child-process-send-the-signal
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void handler(int sig)
{
pid_t pid;
pid = wait(NULL);
printf("Pid %d exited.\n", pid);
}
int main(void)
{
signal(SIGCHLD, handler);
if(!fork())
{
printf("Child pid is %d\n", getpid());
exit(0);
}
printf("Parent pid is %d\n", getpid());
getchar();
return 0;
}
daemon.c
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include "config.h"
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif /* HAVE_SYS_FILE_H */
/*---------------------------------------------------------------------------*\
Static Routines
\*---------------------------------------------------------------------------*/
/* redirect_fds(): redirect stdin, stdout, and stderr to /dev/NULL */
static void redirect_fds()
{
(void) close(0);
(void) close(1);
(void) close(2);
if (open("/dev/null", O_RDWR) != 0)
{
syslog(LOG_ERR, "Unable to open /dev/null: %s", strerror(errno));
exit(1);
}
(void) dup(0);
(void) dup(0);
}
static int do_fork(void)
{
int status = 0;
switch(fork())
{
case 0:
/* This is the child that will become the daemon. */
break;
case -1:
/* Fork failure. */
status = -1;
break;
default:
/* Parent: Exit. */
_exit(0);
}
return status;
}
/*---------------------------------------------------------------------------*\
Public Routines
\*---------------------------------------------------------------------------*/
int daemon(int nochdir, int noclose)
{
int status = 0;
openlog("daemonize", LOG_PID, LOG_DAEMON);
/* Fork once to go into the background. */
if((status = do_fork()) < 0 )
;
/* Create new session */
else if(setsid() < 0) /* shouldn't fail */
status = -1;
/* Fork again to ensure that daemon never reacquires a control terminal. */
else if((status = do_fork()) < 0 )
;
else
{
/* clear any inherited umask(2) value */
umask(0);
/* We're there. */
if(! nochdir)
{
/* Go to a neutral corner. */
chdir("/");
}
if(! noclose)
redirect_fds();
}
return status;
}
网友评论