背景:
NodeJS的特点是单进程,事件驱动,非阻塞式IO编程,当主进程抛异常挂掉,整个NodeJS Server就会停止。
对当前的NodeJS进程进行监控非常重要,NodeJS的进程的停止时,能在指定时间内重启动,继续提供服务。
思路:
1.起一个守护进程,用于与各子进程(虚拟进程)进行心跳通信,官运亨通护进程监测子进程是否有回应,若三次连接后没有回应,则将该进程进行重启。
2.子进程与守护进程进行心跳通信,若守护进程不存在,则子进程自动退出。
示例图:
守护进程:bootstrap.js
/** * @author wubocao * node程序启动入口模块 * 1:设置允许环境当前路径cwd为该文件目录 * 2:启动守护进程,运行主服务 * 3:监听关闭事件,关闭主服务并退出 */ //日志 console.log("start bootstrap"); var path = require("path"); var addDeamon = require("./deamon.js").addDeamon; var file = require.main.filename, path = path.dirname(file); process.chdir(path); var modulesNames = [], args = [], deamons = []; if (process.argv && process.argv.length) { for ( var i = 0, len = process.argv.length; i < len; i++) { if (process.argv[i] == '-m') { var names = process.argv[++i]; if (names) { modulesNames = modulesNames.concat(names.split("|")); } } else if (process.argv[i] == '-ppid') {//过滤掉ppid参数 i++; continue; } else { args.push(process.argv[i]); } } } // 可以在此处设置默认载入默认模块 if (modulesNames.length == 0) { console.log('please defined the modules like: node bootstrap.js -m main1.js -m main2.js'); return; // modulesNames.push('main'); } console.log(modulesNames); modulesNames.forEach(function(moduleName) { deamons.push(addDeamon(moduleName, args)); }); process.on("exit", function() { console.log("parent exit"); deamons.forEach(function(deamon) { deamon.stop(); }); }); process.on("SIGQUIT", function() { console.log("request for exit"); deamons.forEach(function(deamon) { deamon.stop(); }); process.exit(0); });
守护进程新建一个或者多个daemon对象,每一个daemon启动一个新的业务进程:daemon.js
/** * @author wubocao * 守护进程模块 * 使用addDeamon(model,args,option)来添加一个守护进程 * 该函数返回一个守护进程对象,通过调用该对象的stop和init来停止和重新启动该进程 * */ var cp = require("child_process"); var util = require("util"); //对象深拷贝 function copyObj(obj, stack) { stack = stack || []; var t; if (obj == null) { return t; } if (util.isArray(obj)) {// 数组 var instance = copyObj.getStack(obj, stack); if (instance) { return instance; } var len = obj.length; t = new Array(len); stack.push([ obj, t ]); for ( var i = 0; i < len; i++) { t[i] = copyObj(obj[i]); } } else if (typeof obj == "object") { var instance = copyObj.getStack(obj, stack); if (instance) { return instance; } t = {}; stack.push([ obj, t ]); for ( var k in obj) { t[k] = copyObj(obj[k]); } } else { t = obj; } return t; } copyObj.getStack = function(obj, stack) { for ( var i = stack.length; i--;) { if (stack[i][0] === obj) { return stack[i][1]; } } return null; }; // 守护进程对象 function deamon(model, args, option) { if (!model || typeof model != "string") { throw new Error("illegal model argument"); } var __args; if (args) { if (util.isArray(args)) { __args = copyObj(args); } else { __args = [ args ]; } } var __opt; if (typeof option == "object") { __opt = copyObj(option); } else { __opt = {}; } this.__model = model; this.__args = __args; this.__opt = __opt; this.__cpr = null; this.__cprid = 0; this.__heartbeat = 0; this.init(); } deamon.prototype = { init : function() { if (this.__cpr) { return; } this.__kill = false; console.log("deamon init"); var exeTime = this.__opt.timeout; var start = new Date().getTime(); var context = this; (function run() { console.log("process start"); context.__cpr = cp.fork(context.__model, context.__args, context.__opt); context.__cprid = context.__cpr.pid; context.__cpr.on("exit", function(e) { console.log("process exit"); if (context.__kill) { return; } if (exeTime > 0) { var end = new Date().getTime(); if (end - start < exeTime) { run(); } else { context.__cpr = null; context.__cprid = 0; } } else { run(); } }); context.__cpr.on("message", function(message) { if (typeof message == "object") { switch (message.name) { case "proccessInfo":// 进程信息(心跳检查) context.__messageCall && context.__messageCall(message.value); break; case "broadcast":// 经常广播消息 try { context.__cpr.send(message.value); } catch (e) { console.error("broadcast message error:", e); } break; } } }); })(); // 开始监控心跳 this.startHeartbeat(); }, stop : function() { if (this.__cpr) { console.log("deamon stop"); this.__kill = true; this.__cpr.disconnect(); this.__cpr.kill('SIGQUIT'); this.__cpr = null; this.__cprid = 0; } }, stopForce : function() { if (this.__cpr) { console.log("deamon stop force"); this.__kill = true; // this.__cpr.kill('SIGKILL'); cp.exec("kill -9 " + this.__cprid); this.__cpr = null; this.__cprid = 0; } }, getInfo : function(callback, msg) { if (this.__cpr) { this.__messageCall = callback; try { if (msg) { console.log("try get child process info with message[" + msg + "]"); } this.__cpr.send({ name : "proccessInfo", msg : msg || "" }); } catch (e) { console.error("send message 'proccessInfo' error:", e); } } else { console.error("no child process when get child process info"); } }, //开始心跳 startHeartbeat : function() { var deamon = this; //先停掉原来的心跳 this.stopHeartbeat(); //times为监控心跳连续失败次数 var times = 0; //心跳检查 function checkDeamon() { //做1500毫秒等待,判断deamon子进程是否挂掉 var t = setTimeout(function() { times++; t = 0; if (times >= 3) { console.log("heart check with no response more then 3 times,restart now"); times = 0; deamon.stopHeartbeat(); deamon.stopForce(); setTimeout(function() { deamon.init(); }, 1000); } }, 1500); deamon.getInfo(function(memInfo) { if (t != 0) { clearTimeout(t); t = 0; } times = 0; //console.log(memInfo); }, times > 0 ? "retry with times:" + times : ""); } //每5秒获取一下 this.__heartbeat = setInterval(checkDeamon, 5000); }, //停止心跳 stopHeartbeat : function() { this.__heartbeat = this.__heartbeat && clearInterval(this.__heartbeat); } }; exports.addDeamon = function(model, args, option) { args = args || []; // 过滤掉ppid参数 for ( var i = 0, len = args.length; i < len; i++) { if (args[i] == '-ppid') { i++; } } return new deamon(model, args.concat([ '-ppid', process.pid ]), option); }
监控进程monitor.js,此JS由业务JS引入,用于和daemon进行心跳通信,确保进程是活动进程:
require('./monitor/module_listener.js'); (function(){ // 开始心跳,与父进程联系 if (process.argv && process.argv.length) { for ( var i = 0, len = process.argv.length; i < len; i++) { if (process.argv[i] == '-ppid') {// ppid参数,由父进程启动的 console.log('startHB'); startHB(); break; } } } // 开始心跳 function startHB() { // 退出信号处理 process.on("SIGQUIT", function() { console.log("request for exit"); process.exit(0); }); // 与父进程断开联系信号处理 process.on("disconnect", function() { console.log("request for exit"); process.exit(-1); }); // 心跳消息处理 process.on("message", function(message) { console.log('child receive msg: ' + message); if (typeof message == "object") { if (message.name == "proccessInfo") { process.send({ name : "proccessInfo", value : process.memoryUsage() }); } } else if (typeof message === "string") { switch (message) { case "heartbeat":// 心跳回包 if (heartbeatTimer) { times = 0; clearTimeout(heartbeatTimer); heartbeatTimer = 0; } break; } } }); // times为监控心跳连续失败次数 var times = 0, heartbeatTimer; // 心跳检查 function checkParent() { // 做1500毫秒等待,判断deamon子进程是否挂掉 heartbeatTimer = setTimeout(function() { times++; t = 0; if (times >= 3) { times = 0; console.log("heart check with no response more then 3 times,exit now"); process.exit(-1); } }, 1500); times > 0 && console.log("try get parent heartbeat " + times + " times"); //心跳发包 process.send({ name : "broadcast", value : "heartbeat" }); } //每5秒获取下 setInterval(checkParent, 5000); } })();
业务示例:
main1.js:
var http = require('http'); console.log('init main1: pid = ' + process.pid); require('./monitor.js'); http.createServer(function(req, res){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World, main1 \n'); }).listen('8938'); console.log('main1 server running at http://127.0.0.1:8938');
main2.js
var http = require('http'); console.log('init main2: pid = ' + process.pid); require('./monitor.js'); http.createServer(function(req, res){ res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('afdfadfdafdas'); res.end('Hello World main 2\n'); }).listen('8937'); console.log('main2 server running at http://127.0.0.1:8937');
注意:需要在main1.js和main2.js中引入
启动进程:
node bootstrap.js -m main1.js -m main2.js
源码中,还包含了一个node.sh,用于管理start或者是restart, stop 等操作:
sudo chmod +x node.sh
./node.sh bootstrap.js -m main1.js -m main2.js start
相关推荐
首先,`windows下定时重启nodejs程序`这个标题涉及到的关键技术点包括: 1. **Windows计划任务**:Windows操作系统内置了计划任务功能,可以安排程序在特定时间执行,用于定时重启Node.js服务非常实用。通过创建新...
6. **单进程、单线程**:尽管 NodeJS 默认使用单线程,但可以通过工作进程(worker process)来实现多线程处理计算密集型任务。 安装 NodeJS 在 Windows 上相对简单,只需从官方网站下载安装包,按照步骤完成安装,...
这一篇是使用pm2实现nodejs的自动重启。 什么是pm2? 如官网介绍的,pm2是nodejs下先进的,生产进程管理器。如性能监控,自动重启、负载均衡等等。 关于pm2的更多教程,请移步pm2官方文档 1.请确保安装了node 2.安装...
在开发过程中,可以使用`nodemon`工具自动重启服务器以反映代码更改。安装并配置`nodemon`后,用`nodemon server.js`命令启动服务器。 在部署到生产环境时,考虑使用PM2进程管理器确保服务器稳定运行: ```bash ...
可能需要设置环境变量,配置SSL证书以实现HTTPS,以及设置自动重启机制,确保应用在崩溃时能自动恢复。 6. **部署应用**:将构建后的应用文件上传到服务器。这可以通过FTP、SCP、Docker或其他平台提供的部署工具...
当检测到代码更改时,nodemon会自动重启服务,确保服务始终在线。 在上传项目到服务器时,应避免上传整个`node_modules`目录,因为它通常包含大量的依赖包,会延长上传时间。你可以删除`node_modules`,仅上传核心...
7. **PM2**: PM2是一个Node.js进程管理工具,能够实现应用的负载均衡、自动重启、日志管理和集群部署,确保服务的高可用性。 8. **小型工作服务**: 指的是这个项目适合用于中小型企业或个人开发的小型Web服务,它...
3. 进程管理:PM2提供了诸如列出所有进程(`pm2 list`)、重启应用(`pm2 restart all`)、查看日志(`pm2 logs`)等命令,方便开发者进行实时监控和维护。 4. 配置文件:通过创建和编辑pm2的配置文件(如pm2....
3. 安装PM2,这是一个Node.js应用的进程管理器,可确保服务稳定运行: ```bash npm install pm2 -g ``` 接下来,你需要修改源代码中的appid和appSecret,这些可以在微信小程序后台的开发设置中获取。将包含...
6. **开发与部署**:项目可能使用`webpack`或`create-react-app`进行打包,`nodemon`监控文件变化自动重启服务器,`pm2`用于生产环境的进程管理。部署时,需确保Node.js环境安装,然后在服务器上运行启动脚本。 这...
- **PM2**:介绍 PM2,一个生产级别的 Node.js 应用进程管理器,提供自动重启和负载均衡功能。 通过本“Node.js 完整指南”,你将全面掌握 Node.js 开发所需技能,无论你是初学者还是有经验的开发者,都能从中受益...
6. **自动化部署与监控**:项目可能集成了如PM2这样的进程管理工具,用于应用的启动、重启和负载均衡,以及日志管理和性能监控。 7. **版本控制与代码协作**:文件名"node-kingo-server-master"暗示该项目可能使用...
6. **运行与部署**:项目运行时,开发者通常会在命令行中使用`node app.js`或`nodemon app.js`(如果使用nodemon来自动重启服务器)来启动服务。在生产环境中,可以使用PM2这样的进程管理器来确保服务的稳定运行。 ...
14. **性能监控**:`pm2`是一个进程管理工具,可以实现应用的负载均衡、自动重启和性能监控。 15. **CLI工具**:`commander`或`yargs`帮助构建命令行接口,使Node.js程序更易用。 "nodejs-stuff-master"可能包含了...
`nodemon`是一个监控文件更改的工具,当检测到代码变更时,它可以自动重启服务,节省手动重启的时间。在命令行中输入`nodemon`,服务就会启动。如果`nodemon`不是全局安装的,需要使用`npx nodemon`来运行。 ### ...
使用yarn安装pm2后,可以运行pm2 start server.js来启动项目,这样pm2会保持应用运行在后台,并且如果应用崩溃,pm2还可以自动重启它。 以上就是Node.js项目在CentOS Linux服务器上进行线上部署的详细步骤。需要...