You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js HTTP服务器优雅关闭异常:server.close回调未触发

Node.js http.Server 收到SIGINT后无法正常退出(Keep-Alive连接导致)

问题场景

基于Node.js v20.4.0内置的http.Server实现服务器,需求是收到SIGTERM或SIGINT信号时,停止接收新连接、完成正在处理的请求后退出。简化代码(server.js)如下:

const http = require("http");

const requestListener = (_, res) => {
    res.writeHead(200);
    res.end("howdy!");
};

const server = http.createServer(requestListener);
const host = 'localhost'
const port = 8888

const shutdown = () => {
  console.log(`Got SIGINT, calling server.close() ...`);
  server.close(() => {
    console.log('Server closed, exiting')
    process.exit(0)
  })
}

process.on("SIGINT", shutdown)

server.listen(port, host, () => {
    console.log(`Listening on http://${host}:${port}`);
});

异常现象

直接按Ctrl+C发送SIGINT能正常触发退出流程,但按以下步骤操作时,server.close()的回调始终不执行:

  • 执行node server.js启动服务器
  • 在Chrome标签页打开http://localhost:8888
  • 在服务器终端按Ctrl+C

此时终端会打印Got SIGINT, calling server.close() ...,但直到关闭Chrome标签页,才会触发Server closed, exiting并退出。

原因分析

Chrome发送请求时携带了Connection: keep-alive请求头,Node服务器响应也返回了Connection: keep-alive字段,建立了HTTP长连接。根据server.close()的官方文档,该方法仅会停止接收新连接,并关闭未处于请求发送/响应等待状态的连接,但Keep-Alive长连接在请求处理完成后会进入空闲等待状态,不会被自动关闭,导致服务器一直持有这些连接,server.close()的回调无法触发。这对应Node.js仓库的#41578问题,且server.closeIdleConnections()方法也无法解决该问题。

解决方案

方案1:手动关闭空闲长连接(推荐)

修改shutdown函数,在调用server.close()后,主动遍历服务器的所有连接,关闭那些已经完成请求处理的空闲连接:

const shutdown = () => {
  console.log(`Got SIGINT, calling server.close() ...`);
  // 停止接收新连接
  server.close(() => {
    console.log('Server closed, exiting')
    process.exit(0)
  });

  // 遍历并关闭空闲的Keep-Alive连接
  server.getConnections((err, connectionsCount) => {
    if (err) throw err;
    console.log(`Closing ${connectionsCount} idle connections`);
    // 访问服务器的内部连接列表
    server._connections.forEach((conn) => {
      // _httpMessage为null表示当前连接没有正在处理的请求,处于空闲状态
      if (!conn._httpMessage) {
        conn.end();
      }
    });
  });
};

该方案保留了HTTP长连接的性能优势,同时能在收到退出信号时正常关闭服务器。

方案2:禁用Keep-Alive(简单但牺牲性能)

在响应时强制设置Connection: close头,让每次请求完成后直接关闭连接:

const requestListener = (_, res) => {
    res.writeHead(200, { 'Connection': 'close' });
    res.end("howdy!");
};

此方法实现简单,但会禁用HTTP长连接,影响高频请求场景下的性能。

内容的提问来源于stack exchange,提问作者grumbler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:20:20