我继承了一个有点混乱的node.js / Express应用程序。它经常被相当随机地卡住,并且在重新启动之前没有响应任何请求。
我怀疑应用程序中的某些东西是阻塞的,要么陷入循环,要么在没有使用正确的异步技术的情况下向外部api发出请求,并且永远不会得到响应,并且永远不会在服务器停止响应时超时但不会崩溃。
我显然希望找到罪魁祸首代码并解决问题,但同时我想找到一种方法在停止响应时自动重启服务器。
要在本地测试解决方案(因为我目前不知道实际的罪魁祸首),我创建了以下Express路线,它模拟了我得到的确切行为。
app.get('/block-block-block', function (req, res){
for(;;) {}
};
我遇到的问题是上面的路由被命中(这会立即阻止服务器响应任何东西),有没有办法在内部检测节点中的阻塞并重新启动或关闭?如果没有什么是检查服务器何时没有响应并重新启动它的好方法?
我所做的大部分搜索都引导我使用forever和PM2等工具。如果您的应用程序崩溃,这些工作很有效,但是当应用程序无法阻止时,我没有看到任何重新启动的功能。
答案 0 :(得分:3)
我想出了如何使用本机节点功能来解决这个问题。 Migg的答案很好并且引导我朝着正确的方向前进,但它仍然没有显示当事件循环被完全阻止时如何自动重启。
诀窍是使用Node的本地child_process模块和fork method从另一个节点实例启动服务器,并让该实例ping服务器以获取响应,并在卡住时重新启动它。这类似于Forever和PM2的工作方式。很难相信这些库中没有一种简单的方法可以实现这一点,但这就是你如何能够天真地做到这一点。
我已经对这段代码进行了大量评论,以指出一切正在做什么。另请注意,如果您不熟悉,我正在使用ES2015的Arrow Functions. Go了解它们。
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。
现在,在您的服务器上添加以下内容以响应心跳请求。
//listen and respond to heartbeat request from parent
process.on('message', (message) => {
if(message && message.request === 'heartbeat') {
process.send({heartbeat: 'thump'});
}
});
最后添加一个超时来测试它是否有效(不适用于生产!)
//block the even loop after 30 seconds
setTimeout(() => {
for(;;){}
}, 30000);
答案 1 :(得分:2)
首先,您应该通过查看代码来尝试查找代码中的问题。
对于正在运行的应用,您应该使用pm2 start big-array.js --max-memory-restart 20M
。它有一个设置,可以根据过多的内存消耗重新启动应用程序。直接from the docs:
ecosystem.json
或使用{
"max_memory_restart" : "20M"
}
:
process.nextTick
还有several great articles关于调试node.js中的内存泄漏以便在线查找。甚至还有module that reports leaks我们在早期使用过的process.nextTick
。这是一个太大的主题,无法填补它。
您可以检测应用以报告事件循环的响应性。因此,如果某些代码阻塞循环太长时间,您可以以编程方式终止该进程。你必须看https://www.joyent.com/developers/node/debug。
您可以引入一个测量值,例如每隔X秒调用process.exit(1)
,如果它需要超过一些定义的时间,请发送pm2
以终止该过程并让process.exit
重新启动它
这样做的好处是你的应用程序大部分时间都在运行。缺点是所有具有开放连接的用户在调用l[::3]
时都不会得到答案。
要查找正在运行的代码中的内存泄漏和其他问题,您应该深入了解{{3}}。关于MDB的整个部分将帮助您找到问题,但需要一些时间并习惯它。所有这些都是太多的信息,不能在这里链接到它。
祝你的应用好运!
答案 2 :(得分:1)
我遇到过一次或两次这个问题,答案一直是处理一个独立的监控服务,该服务定期向端点发送请求。在这么多失败或超时请求之后,服务将重新启动服务器。
然而,它并没有缺点。最明显的是您的应用程序在重新启动之前必须失败或达到某个阈值。这意味着它可能会在重新启动之前的几分钟甚至几小时内停止生产,具体取决于您的阈值。然而另一种方法是等待应用程序的消费者开始抱怨,因为他们很可能是你的客户,这可能会更糟糕。答案 3 :(得分:0)
节点死亡的常见问题是如果我们不能正确处理诺言,我们必须处理诺言拒绝。
因此,每当进行异步调用时,请始终使用try and catch块来处理承诺拒绝。