使用命令行的方式運(yùn)行nodejs web應(yīng)用,一旦程序有bug造成進(jìn)程崩潰,應(yīng)用就不能繼續(xù)提供服務(wù)了;并且,nodejs是單線程的模式,不能充分利用服務(wù)器的多核資源。使用node的cluster模塊可以監(jiān)控應(yīng)用進(jìn)程,退出后重新啟動(dòng)node應(yīng)用進(jìn)程,并可以啟動(dòng)多個(gè)node應(yīng)用進(jìn)程,做到負(fù)載均衡,充分利用資源。
啟動(dòng)單個(gè)進(jìn)程
node bin/www
文件www的內(nèi)容為:
/**
* @fileOverview 應(yīng)用程序啟動(dòng)文件入口
* @author --
*/
"use strict";
const app = require('../app');
const http = require('http');
const logger = require("../logger").appLogger();
/**
* 設(shè)置程序監(jiān)聽(tīng)端口
*/
let port = normalizePort(config.port || process.env.PORT || '3001');
app.set('port', port);
/**
* 創(chuàng)建http服務(wù)實(shí)例
*/
let server = http.createServer(app);
/**
* 開(kāi)啟服務(wù)監(jiān)聽(tīng)
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* 格式化監(jiān)聽(tīng)端口
*/
function normalizePort(val) {
let port = parseInt(val, 10);
if (isNaN(port)) {
/** named pipe */
return val;
}
if (port >= 0) {
/** port number */
return port;
}
return false;
}
/**
* 啟動(dòng)錯(cuò)誤事件處理
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
let bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
/** 錯(cuò)誤提示 */
switch (error.code) {
case 'EACCES':
logger.error(bind + ' 權(quán)限不足');
process.exit(1);
break;
case 'EADDRINUSE':
logger.error(bind + ' 端口被占用');
process.exit(1);
break;
default:
throw error;
}
}
/**
* 正常啟動(dòng)事件處理
*/
function onListening() {
let addr = server.address();
let bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
logger.info('服務(wù)啟動(dòng)成功');
logger.info('Listening on ' + bind);
}
cluster多進(jìn)程模式
node bin/cluster.js
文件cluster.js的內(nèi)容為:
/**
* @fileOverview 應(yīng)用程序多進(jìn)程啟動(dòng)入口
* @author --
*/
"use strict";
const cluster = require('cluster');
const cpus = require('os').cpus();
const accessLogger = require("../logger").accessLogger();
accessLogger.info('master ' + process.pid + ' is starting.');
cluster.setupMaster({
/* 應(yīng)用進(jìn)程啟動(dòng)文件 */
exec: 'bin/www'
});
/* 啟動(dòng)應(yīng)用進(jìn)程個(gè)數(shù)和服務(wù)器CPU核數(shù)一樣 */
for (let i = 0; i < cpus.length; i++) {
cluster.fork();
}
cluster.on('online', function (worker) {
/* 進(jìn)程啟動(dòng)成功 */
accessLogger.info('worker ' + worker.process.pid + ' is online.');
});
cluster.on('exit', function (worker, code, signal) {
/* 應(yīng)用進(jìn)程退出時(shí),記錄日志并重啟 */
accessLogger.info('worker ' + worker.process.pid + ' died.');
cluster.fork();
});
其他多進(jìn)程方案
直接使用cluster模塊管理nodejs進(jìn)程,要在生產(chǎn)環(huán)境中使用,還要做很多工作,屬于重復(fù)造輪子?,F(xiàn)在比較流行的nodejs進(jìn)程監(jiān)控和多進(jìn)程管理的工具有supervisor、forever、pm2等,下一篇將就pm2和docker談一談nodejs web應(yīng)用在生產(chǎn)環(huán)境中的部署。