Node.js:什么是自动重启没有响应的节点服务器的好方法?

时间:2016-02-03 19:04:54

标签: node.js express

我继承了一个有点混乱的node.js / Express应用程序。它经常被相当随机地卡住,并且在重新启动之前没有响应任何请求。

我怀疑应用程序中的某些东西是阻塞的,要么陷入循环,要么在没有使用正确的异步技术的情况下向外部api发出请求,并且永远不会得到响应,并且永远不会在服务器停止响应时超时但不会崩溃。

我显然希望找到罪魁祸首代码并解决问题,但同时我想找到一种方法在停止响应时自动重启服务器。

要在本地测试解决方案(因为我目前不知道实际的罪魁祸首),我创建了以下Express路线,它模拟了我得到的确切行为。

app.get('/block-block-block', function (req, res){ 
  for(;;) {}
};

我遇到的问题是上面的路由被命中(这会立即阻止服务器响应任何东西),有没有办法在内部检测节点中的阻塞并重新启动或关闭?如果没有什么是检查服务器何时没有响应并重新启动它的好方法?

我所做的大部分搜索都引导我使用foreverPM2等工具。如果您的应用程序崩溃,这些工作很有效,但是当应用程序无法阻止时,我没有看到任何重新启动的功能。

4 个答案:

答案 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块来处理承诺拒绝。