Node.js:自动重启没有响应的节点服务器的好方法是什么?
我继承了一个node.js/Express应用程序,有点混乱.它定期且相当随机地卡住,直到重新启动后才响应任何请求.
I've inherited a node.js/Express app that is a bit of a mess. It's regularly and fairly randomly getting stuck and not responding to any request until it is restarted.
我怀疑应用程序中的某些内容正在阻塞,或者陷入了循环,或者在未使用适当的异步技术的情况下向外部api发出请求,并且从未获得响应,也从未在服务器停止响应时才停止响应但不会崩溃.
I suspect that something within the app is blocking and either getting stuck in a loop or making a request to an external api without using proper Async techniques, and never getting a response and never timing out at witch point the server just stops responding but doesn't crash.
我显然想找到罪魁祸首代码并解决问题,但是与此同时,我想找到一种在服务器停止响应时自动重新启动服务器的方法.
I would obviously like to find the culprit code and fix the problem, however in the mean time I would like to find a way to automatically restart the server when it stops responding.
要在本地测试解决方案(因为我目前不知道实际的罪魁祸首),我创建了以下Express路由,该路由模拟了我得到的确切行为.
To test out solutions locally (since I don't currently know the actual culprit) I have created the following Express route which simulates the exact behavior I'm getting.
app.get('/block-block-block', function (req, res){
for(;;) {}
};
我的问题是使上述路由被击中(这将立即使服务器停止对任何事情的响应),是否有办法在内部检测节点的阻塞并重新启动或关闭?如果不是,那么什么时候检查服务器没有响应并重新启动服务器的好解决方案是什么呢?
The question I have is give the above route being hit (which immediately stops the server from responding to anything), is there a way to detect the blockage in node internally and restart or shut down? And if not what is a good solution for checking when the server is not responding and restarting it?
我所做的大多数搜索都将我带到诸如永远和
Most searching I have done leads me to tools like forever and PM2. These work great if your app crashes but I don't really see any fuctionality for restarting when an app is radomley blocking.
我想出了如何使用本机Node功能解决此问题的方法. Migg的回答很好,可以引导我朝正确的方向前进,但是它仍然没有显示出在事件循环被完全阻止时如何自动重新启动.
I figured out how to solve this using native Node functionality. Migg's answer was good and lead me in the right direction, but it still doesn't show how to automatically restart when the event loop is completely blocked.
诀窍是使用Node的本机 child_process 模块和
The trick is to use Node's native child_process module and the fork method to start the server from another node instance and have that instance ping the server for responses, restarting it when it's stuck. This is similar to how Forever and PM2 work. It's hard to believe there's not a simple way to implement this with either of those libraries, but this is how you can do it naively.
我对这段代码进行了评论,以指出一切.另请注意,我正在使用ES2015的箭头函数.如果您不熟悉,请阅读有关它们的内容.
I have commented this code heavily to point out what everything is doing. Also note that I am using ES2015's Arrow Functions. Go read about them if you're not familiar.
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();
请确保使用要运行的任何Node应用程序将server.js更改为.
Be sure to change out server.js with whatever Node app you want to run.
现在,在服务器上添加以下内容以响应心跳请求.
Now on your server add the following to respond to the heartbeat request.
//listen and respond to heartbeat request from parent
process.on('message', (message) => {
if(message && message.request === 'heartbeat') {
process.send({heartbeat: 'thump'});
}
});
最后添加一个超时来测试它是否有效(不适用于生产!)
Finally add a timeout to test that it works (not for production!)
//block the even loop after 30 seconds
setTimeout(() => {
for(;;){}
}, 30000);