1

我正在优雅地关机。我的应用程序的 API 需要 10 -20 秒才能响应

server.close(
    () => {
      log('HTTP server closed')
    }
  );

上面的代码在 API 调用之间存在时间间隔时有效,但它永远不会停止我的服务器,因为我的服务器在响应旧请求之前收到了新请求。

4

2 回答 2

1

node.close()方法几乎完全按照您的要求执行。具体来说:

server.close([callback])
添加于:v0.1.90
callback 关闭服务器时调用。
Returns: <net.Server>
停止服务器接受新连接并保持现有连接。

您可能遇到的问题是,有人通过使用 keepalive 的现有连接发送新请求。实际上并没有创建新的连接,但效果是一样的

我发现解决此问题的唯一方法是主动跟踪所有打开的连接以及所述连接上的每个请求,然后在关闭时跟踪:

  1. 强制关闭没有活动请求的连接connection.destroy()
  2. 拒绝在现有连接上收到的请求request.res.end()

实际逻辑看起来像这样,这里缺少逻辑,但这应该让您足够接近以解决它。

const connections = [];
let shuttingDown = false;

server.on('connection', (conn) => {
  let connectionId = 'some unique id here';
  
  conn.connectionId = connectionId;
  conn.requests = []; // so we can track open requests
  
  conn.on('close' => {
    delete connections[connectionId];
  });
  
  connections[connectionId] = conn;  
});

server.on('request', (req) => {
  // I don't actually know if the req.connection.connectionId will exist here
  // due to possible race conditions, or immutability of the connection object
  // if that is the case you may need to find another way to determine a unique
  // identifier based on existing connection fields
  let conn = connections[req.connection.connectionId];
  conn.requests.push(req);
  
  function requestComplete() {
    // if connection still exists
    // logic here for deleting request from connection.requests array
    // if shutting down, and connection.requests.length = 0; then connection.end()
  }
  
  req.res.on('finish', requestComplete);
  req.res.on('close', requestComplete);
  req.res.on('end', requestComplete);
  
  // If the server is already shutting down before this request is received
  // We do this after adding listeners for the request in case this is the only
  // request for the connection, so that our existing logic will auto-close the
  // socket instead of needing to duplicate it here as a special case
  if (shuttingDown) {
    req.res.statusCode = 503;
    return req.res.end();
  }
});

function shutdown() {
  shuttingDown = true;
  server.close(() => {
    console.log('closed');
  });
}

推荐升级:

  • 如果花费的时间超出合理范围,则在关闭期间记录打开的连接及其请求
  • 在预定时间后强制关闭请求和连接(可能想要记录哪些被强制,以便您知道某些请求是否卡住)
  • 检查是否有任何 longpoll 请求,这些请求是不确定的,因此必须强制终止(text/event-stream标头或您知道的路径仅服务于该内容)
于 2021-11-11T06:02:21.877 回答
1

您可以实施中间件,在您开始关闭过程后立即拒绝传入连接。

// set this flag to true when you want to start 
//   immediately rejecting new connections
let pendingShutdown = false;

// first middleware
app.use((req, res, next) => {
   if (pendingShutdown) {
       // immediately reject the connection
       res.sendStatus(503);
   } else {
       next();
   }
});

一旦正在处理的连接完成后就不再长时间运行连接,服务器应该在您执行此操作时找到一个自然退出点:

pendingShutdown = true;
server.close();

NPM 上还有一些模块也提供了各种关闭算法。

然后,为了防止任何长期卡住的连接阻止您的服务器关闭,您可以在现有连接上设置超时,或者只设置全局超时并process.exit()在超时后执行(强制关闭)。

于 2021-11-11T06:03:45.780 回答