我继承了一个node.js / Express应用程序,有点混乱。它定期且相当随机地卡住,直到重新启动后才响应任何请求。
我怀疑应用程序中的某些内容正在阻塞,或者陷入了循环或在没有使用适当的异步技术的情况下向外部api发出请求,并且从没有得到响应并且从不超时,服务器只是停止响应,但是没有崩溃。
我显然想找到罪魁祸首代码并解决问题,但是与此同时,我想找到一种在服务器停止响应时自动重新启动服务器的方法。
为了在本地测试解决方案(因为我目前不知道实际的罪魁祸首),我创建了以下Express路由,该路由模拟了我得到的确切行为。
app.get('/block-block-block', function (req, res){
for(;;) {}
};
Run Code Online (Sandbox Code Playgroud)
我的问题是使上述路由被击中(这将立即使服务器停止对任何事情的响应),是否有办法在内部检测节点中的阻塞并重新启动或关闭?如果不是,那么什么时候检查服务器没有响应并重新启动服务器的好解决方案是什么?
我所做的大多数搜索都将我带到诸如everever和 PM2之类的工具。如果您的应用程序崩溃,这些功能会很好用,但是当应用程序受到radomley阻止时,我真的看不到任何重启功能。
我想出了如何使用本机节点功能解决此问题的方法。Migg的回答很好,可以引导我朝正确的方向发展,但是它仍然没有显示出在事件循环被完全阻止时如何自动重新启动。
诀窍是使用Node的本机child_process模块和fork方法从另一个节点实例启动服务器,并使该实例ping服务器以获取响应,并在卡住时重新启动它。这类似于Forever和PM2的工作方式。很难相信没有一个简单的方法可以用这两个库中的任何一个来实现,但这是您天真地做到的方式。
我对这段代码进行了严重评论,以指出一切。另请注意,我正在使用ES2015的Arrow功能。如果您不熟悉,请阅读有关它们的内容。
var fork = require('child_process').fork;
var server, heartbeat;
function startServer () {
console.log('Starting server');
server = fork('server');
//when the server goes down restart it
server.on('close', (code) => {
startServer();
});
//when server sends a heartbeat message save it
server.on('message', (message) => {
heartbeat = message ? message.heartbeat : null;
});
//ask the server for a heartbeat
server.send({request: 'heartbeat'});
//wait 5 seconds and check if the server responded
setTimeout(checkHeartbeat, 5000);
}
function checkHeartbeat() {
if(heartbeat) {
console.log('Server is alive');
//clear the heart beat and send request for a new one
heartbeat = null;
server.send({request: 'heartbeat'});
//set another hearbeat check
setTimeout(checkHeartbeat, 5000);
} else {
console.log('Server looks stuck...killing');
server.kill();
}
}
startServer();
Run Code Online (Sandbox Code Playgroud)
确保使用您要运行的任何Node应用程序更改server.js。
现在,在您的服务器上添加以下内容以响应心跳请求。
//listen and respond to heartbeat request from parent
process.on('message', (message) => {
if(message && message.request === 'heartbeat') {
process.send({heartbeat: 'thump'});
}
});
Run Code Online (Sandbox Code Playgroud)
最后添加一个超时来测试它是否有效(不适用于生产!)
//block the even loop after 30 seconds
setTimeout(() => {
for(;;){}
}, 30000);
Run Code Online (Sandbox Code Playgroud)