自上一篇 pm2 部署介绍 后,有面试官问道不用 pm2 做进程守护,该怎么办?
由于 NodeJs 是单线程执行的,所以主线程抛出了一个错误就会退出程序。线上部署当然不可能出了异常就退出了,所以需要守护进程。
node-forever
使用 forever start simple-server.js
其原理就是崩溃就重启一个。
Monitor.prototype.start = function (restart) {
var self = this,
child;
// 非重启则返回自身进程
if (this.running && !restart) {
process.nextTick(function () {
self.emit('error', new Error('Cannot start process that is already running.'));
});
return this;
}
// 重启标志传入时,重新 fork 进程,方法里使用了 child_process.spawn 执行命令来衍生一个新进程
child = this.trySpawn();
if (!child) {
process.nextTick(function () {
self.emit('error', new Error('Target script does not exist: ' + self.args[0]));
});
return this;
}
this.ctime = Date.now();
this.child = child;
this.running = true;
this.isMaster = cluster.isMaster;
process.nextTick(function () {
self.emit(restart ? 'restart' : 'start', self, self.data);
});
function onMessage(msg) {
self.emit('message', msg);
}
// Re-emit messages from the child process
this.child.on('message', onMessage);
// 监听退出事件,崩溃时退出也算。
child.on('exit', function (code, signal) {
var spinning = Date.now() - self.ctime < self.minUptime;
child.removeListener('message', onMessage);
self.emit('exit:code', code, signal);
function letChildDie() {
self.running = false;
self.forceStop = false;
self.emit('exit', self, spinning);
}
function restartChild() {
self.forceRestart = false;
process.nextTick(function () {
self.start(true);
});
}
self.times++;
// 强制关闭,当重启次数过多
if (self.forceStop || (self.times >= self.max && !self.forceRestart)
|| (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
letChildDie();
}
// 按照最小的重启时间间隔,防止不停崩溃重启
else if (spinning) {
setTimeout(restartChild, self.spinSleepTime);
}
else {
restartChild();
}
});
// 返回重启后的新进程
return this;
};
shell 脚本启动守护 node
实例
WEB_DIR='/var/www/ourjs'
WEB_APP='svr/ourjs.js'
#location of node you want to use
NODE_EXE=/root/local/bin/node
while true; do
{
$NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js
echo "Stopped unexpected, restarting
"
} 2>> $WEB_DIR/error.log
sleep 1
done
这个文件非常简单,只有启动的选项,守护的核心功能是由一个无限循环 while true; 来实现的,为了防止过于密集的错误阻塞进程,每次错误后间隔1秒重启服务。错误日志记录也非常简单,直接将此进程控制台当中的错误输出到error.log文件即可: 2>> $WEB_DIR/error.log 这一行, 2 代表 Error。
cluster API
Node 原生提供了 cluster,可以创建共享服务器端口的子进程。就如 EggJS 官网多进程模型的说明
+---------+ +---------+
| Worker | | Master |
+---------+ +----+----+
| uncaughtException |
+------------+ |
| | | +---------+
| <----------+ | | Worker |
| | +----+----+
| disconnect | fork a new worker |
+-------------------------> + ---------------------> |
| wait... | |
| exit | |
+-------------------------> | |
| | |
die | |
| |
| |
可以再业务代码之上起一个 master 进程,他 fork 出多个 worker 进程来处理任务,每当一个 worker 挂了,会有事件传回给 master,master 就能重新 fork 一份新的 worker。
那么只要 master 不挂,就能达到守护进程的目的。