介紹
Supervisor 是一個(gè)由 Python 實(shí)現(xiàn)酪耕,基于 client/server 模型的系統(tǒng)呜象,用于監(jiān)控和管理進(jìn)程。目前晚岭,只支持在 *inx 系統(tǒng)下運(yùn)行鸥印。
它主要由四個(gè)組件組成,分別是:
- supervisord(server 端)
- supervisorctl(client 端)
- XML-RPC API (client 端)
- Web UI(client 端)
使用場(chǎng)景
- 保證服務(wù)的可用(autostart=true坦报,autorestart=unexpected)
- 多進(jìn)程(numprocs=num库说,num>1)
- 后臺(tái)進(jìn)程(沒(méi)使用 supervisord 之前,一般通過(guò) nohup command & 實(shí)現(xiàn))
安裝
這里使用 pip 安裝 supervisor片择。沒(méi)有安裝 pip 的話潜的,使用下面的步驟安裝。已有請(qǐng)忽略字管。
# 安裝 pip
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python get-pip.py
# 使用 pip 安裝 supervisor
$ sudo pip install supervisor
使用
配置及啟動(dòng) supervisord
# 創(chuàng)建需要的目錄結(jié)構(gòu)
$ sudo mkdir -pv /etc/supervisor.d/conf.d
# 生成默認(rèn)的配置文件
$ 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
# 啟動(dòng) supervisord
$ sudo supervisord -c /etc/supervisor.d/supervisord.conf
啟動(dòng) supervisord 之后啰挪,會(huì)產(chǎn)生如下的進(jìn)程:
$ 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 的簡(jiǎn)單使用
supervisorctl -c /etc/supervisord.conf
上面這個(gè)命令會(huì)進(jìn)入 supervisorctl 的 shell 界面,然后可以執(zhí)行不同的命令了:
# 查看程序狀態(tài)
> status
# 關(guān)閉 usercenter 程序
> stop usercenter
# 啟動(dòng) usercenter 程序
> start usercenter
# 重啟 usercenter 程序
> restart usercenter
# 讀取有更新(增加)的配置文件嘲叔,不會(huì)啟動(dòng)新添加的程序
> reread
# 重啟配置文件修改過(guò)的程序
> 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;
});
注意問(wèn)題
1. 子進(jìn)程不能是守護(hù)進(jìn)程
對(duì)應(yīng)錯(cuò)誤: Exited too quickly
子進(jìn)程在退出時(shí)亡呵,Supervisord
會(huì)收到 SIGCHLD
信號(hào),接著去執(zhí)行相應(yīng)的操作硫戈。
而守護(hù)進(jìn)程一般會(huì)在 fork 之后锰什,exit 掉父進(jìn)程。
2. 內(nèi)存泄漏
- 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;
}