第二次迭代之后,服务器本身的功能和性能已经得到了初步满足。接下来我们需要从稳定性的角度重新审视一下代码,看看还需要做些什么。
## 设计
从工程角度上讲,没有绝对可靠的系统。即使第二次迭代的代码经过反复检查后能确保没有bug,也很难说是否会因为NodeJS本身,或者是操作系统本身,甚至是硬件本身导致我们的服务器程序在某一天挂掉。因此一般生产环境下的服务器程序都配有一个守护进程,在服务挂掉的时候立即重启服务。一般守护进程的代码会远比服务进程的代码简单,从概率上可以保证守护进程更难挂掉。如果再做得严谨一些,甚至守护进程自身可以在自己挂掉时重启自己,从而实现双保险。
因此在本次迭代时,我们先利用NodeJS的进程管理机制,将守护进程作为父进程,将服务器程序作为子进程,并让父进程监控子进程的运行状态,在其异常退出时重启子进程。
## 实现
根据以上设计,我们编写了守护进程需要的代码。
~~~
var cp = require('child_process');
var worker;
function spawn(server, config) {
worker = cp.spawn('node', [ server, config ]);
worker.on('exit', function (code) {
if (code !== 0) {
spawn(server, config);
}
});
}
function main(argv) {
spawn('server.js', argv[0]);
process.on('SIGTERM', function () {
worker.kill();
process.exit(0);
});
}
main(process.argv.slice(2));
~~~
此外,服务器代码本身的入口函数也要做以下调整。
~~~
function main(argv) {
var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')),
root = config.root || '.',
port = config.port || 80,
server;
server = http.createServer(function (request, response) {
...
}).listen(port);
process.on('SIGTERM', function () {
server.close(function () {
process.exit(0);
});
});
}
~~~
我们可以把守护进程的代码保存为`daemon.js`,之后我们可以通过`node daemon.js config.json`启动服务,而守护进程会进一步启动和监控服务器进程。此外,为了能够正常终止服务,我们让守护进程在接收到`SIGTERM`信号时终止服务器进程。而在服务器进程这一端,同样在收到`SIGTERM`信号时先停掉HTTP服务再正常退出。至此,我们的服务器程序就靠谱很多了。