【问题标题】:How do I shutdown a Node.js http(s) server immediately?如何立即关闭 Node.js http(s) 服务器?
【发布时间】:2013-01-15 14:30:51
【问题描述】:

我有一个包含 http(s) 服务器的 Node.js 应用程序。

在特定情况下,我需要以编程方式关闭此服务器。我目前正在做的是调用它的close() 函数,但这无济于事,因为它会等待任何保持活动的连接首先完成。

因此,基本上,这会关闭服务器,但仅在至少 120 秒的等待时间之后。但我希望服务器立即关闭——即使这意味着中断当前处理的请求。

我做不到的就是简单

process.exit();

因为服务器只是应用程序的一部分,应用程序的其余部分应保持运行。我正在寻找的是概念上的东西,例如 server.destroy(); 或类似的东西。

我怎样才能做到这一点?

PS:连接的保持活动超时通常是必需的,因此减少此时间不是一个可行的选择。

【问题讨论】:

标签: node.js http connection keep-alive


【解决方案1】:

诀窍是您需要订阅服务器的connection 事件,该事件会为您提供新连接的套接字。您需要记住这个套接字,然后在调用 server.close() 之后直接使用 socket.destroy() 销毁该套接字。

此外,您需要监听套接字的close 事件以将其从数组中删除,如果它自然离开,因为它的保活超时确实用完了。

我编写了一个小示例应用程序,您可以使用它来演示此行为:

// Create a new server on port 4000
var http = require('http');
var server = http.createServer(function (req, res) {
  res.end('Hello world!');
}).listen(4000);

// Maintain a hash of all connected sockets
var sockets = {}, nextSocketId = 0;
server.on('connection', function (socket) {
  // Add a newly connected socket
  var socketId = nextSocketId++;
  sockets[socketId] = socket;
  console.log('socket', socketId, 'opened');

  // Remove the socket when it closes
  socket.on('close', function () {
    console.log('socket', socketId, 'closed');
    delete sockets[socketId];
  });

  // Extend socket lifetime for demo purposes
  socket.setTimeout(4000);
});

// Count down from 10 seconds
(function countDown (counter) {
  console.log(counter);
  if (counter > 0)
    return setTimeout(countDown, 1000, counter - 1);

  // Close the server
  server.close(function () { console.log('Server closed!'); });
  // Destroy all open sockets
  for (var socketId in sockets) {
    console.log('socket', socketId, 'destroyed');
    sockets[socketId].destroy();
  }
})(10);

基本上,它所做的就是启动一个新的 HTTP 服务器,从 10 数到 0,然后在 10 秒后关闭服务器。如果没有建立连接,服务器会立即关闭。

如果一个连接已经建立并且它仍然是打开的,它就会被销毁。 如果它已经自然死亡,则在那个时间点只打印一条消息。

【讨论】:

  • 应该socket.on("close",...)socket.once("close",...)
  • 或者,正如下面评论中提到的,使用github.com/hunterloftis/stoppable - 它可能比上面的代码工作得更好,并且可以节省墨水!
  • 答案可以更新:sockets 变量可以像const sockets = new Set(); sockets.add(socket); sockets.forEach((socket) => socket.destroy()); 一样使用Set
  • 如果我能给你一百万票就好了。我编写的一个 Express.js 小测试在加载包含外部 JavaScript 的页面并使用 JSDOM 解析它们时非常慢。我认为 JSDOM 是doing some keep-alive shenanigans。但是按照您的建议销毁套接字可以解决问题。
【解决方案2】:

我找到了一种无需跟踪连接或强制关闭连接的方法。我不确定它在 Node 版本中的可靠性如何,或者是否对此有任何负面影响,但它似乎对我正在做的事情非常有效。诀窍是在调用close 方法后立即使用setImmediate 发出“关闭”事件。这样做是这样的:

server.close(callback);
setImmediate(function(){server.emit('close')});

至少对我来说,这最终释放了端口,以便我可以在调用回调时启动一个新的 HTTP(S) 服务(这几乎是立即的)。现有连接保持打开状态。我正在使用它在更新 Let's Encrypt 证书后自动重启 HTTPS 服务。

【讨论】:

  • 根据您的回答,我使用server.close(); setImmediate(callback);
  • 你能详细说明你在这里做什么,为什么它有效吗?在我看来,调用server.close() 并在回调中打开一个新连接将允许您在侦听新连接的同时保留现有连接。你的代码示例看起来很粗略......你不应该像这样在服务器上发出事件,所以我很好奇你是如何得出这个结论的。
  • @Brad 我一直在坦白使用它的粗略性。这只是我当时的一个想法,试图解决在所有连接关闭之前不会调用回调的问题(正如问题和其他答案所证明的那样)。当然,我可以直接使用 setImmediate 的回调,正如您上面的评论所暗示的那样,但这并不能保证不会引起任何问题,因为一些重要的清理操作可能取决于关闭事件。因此,我认为仅仅发出事件并不是一个坏主意。
  • 非常好用,不需要跟踪连接,也不需要确保生产中的性能不受影响。
【解决方案3】:

如果你需要在关闭服务器后保持进程处于活动状态,那么 Golo Roden 的解决方案可能是最好的。

但是,如果您在正常关闭进程的过程中关闭服务器,您只需要这样做:

var server = require('http').createServer(myFancyServerLogic);

server.on('connection', function (socket) {socket.unref();});
server.listen(80);

function myFancyServerLogic(req, res) {
    req.connection.ref();

    res.end('Hello World!', function () {
        req.connection.unref();
    });
}

基本上,您的服务器使用的套接字只会在它们实际为请求提供服务时使进程保持活动状态。当他们只是无所事事地坐在那里(因为 Keep-Alive 连接),调用server.close() 将关闭进程,只要没有其他东西保持进程活着。如果您在服务器关闭后需要做其他事情,作为您正常关机的一部分,您可以挂接到process.on('beforeExit', callback) 以完成您的正常关机程序。

【讨论】:

  • 这实际上看起来是个好主意,但由于某种原因,它不适用于 Node v7.10.0 和 Express 4.15.2。你测试过吗?
  • 现在使用 Node 14 为我工作,但我没有使用任何框架。太棒了,谢谢!
【解决方案4】:

https://github.com/isaacs/server-destroy 库提供了一种简单的方法来destroy() 具有问题中所需行为的服务器(通过跟踪打开的连接并在服务器销毁时销毁每个连接,如其他答案中所述)。

【讨论】:

【解决方案5】:

正如其他人所说,解决方案是跟踪所有打开的套接字并手动关闭它们。我的节点包killable 可以为你做到这一点。一个示例(使用 express,但您可以在任何 http.server 实例上调用 use killable):

var killable = require('killable');

var app = require('express')();
var server;

app.route('/', function (req, res, next) {
  res.send('Server is going down NOW!');

  server.kill(function () {
    //the server is down when this is called. That won't take long.
  });
});

var server = app.listen(8080);
killable(server);

【讨论】:

    【解决方案6】:

    另一个执行关闭终止连接的 nodejs 包:http-shutdown,在撰写本文时(2016 年 9 月)似乎得到合理维护,并在 NodeJS 6.x 上为我工作

    来自文档

    用法

    目前有两种方法可以使用这个库。首先是对 Server 对象的显式包装:

    // Create the http server
    var server = require('http').createServer(function(req, res) {
      res.end('Good job!');
    });
    
    // Wrap the server object with additional functionality.
    // This should be done immediately after server construction, or before you start listening.
    // Additional functionailiy needs to be added for http server events to properly shutdown.
    server = require('http-shutdown')(server);
    
    // Listen on a port and start taking requests.
    server.listen(3000);
    
    // Sometime later... shutdown the server.
    server.shutdown(function() {
      console.log('Everything is cleanly shutdown.');
    });
    

    第二个是向 Server 对象隐式添加原型功能:

    // .extend adds a .withShutdown prototype method to the Server object
    require('http-shutdown').extend();
    
    var server = require('http').createServer(function(req, res) {
      res.end('God job!');
    }).withShutdown(); // <-- Easy to chain. Returns the Server object
    
    // Sometime later, shutdown the server.
    server.shutdown(function() {
      console.log('Everything is cleanly shutdown.');
    });
    

    【讨论】:

      【解决方案7】:

      我最好的猜测是手动终止连接(即强制关闭它的套接字)。

      理想情况下,这应该通过挖掘服务器的内部结构并手动关闭它的套接字来完成。或者,可以运行一个执行相同操作的 shell 命令(前提是服务器具有适当的权限 &c。)

      【讨论】:

      • 问题是:如何在不使用任何未记录行为的情况下访问连接的套接字?
      • 基本上,您的回答帮助我走上了正轨。请参阅我如何解决它的答案(不使用任何未记录的行为;-))。无论如何,为正确的想法 +1 :-)
      • 这就是我含糊不清的原因。这取决于您使用什么(如果有的话)包装库(例如Express)以及您使用的 Node.js/libraries 版本(API 随时间而变化)
      【解决方案8】:

      const Koa = require('koa')
      const app = new Koa()
      
      let keepAlive = true
      app.use(async (ctx) => {
        let url = ctx.request.url
      
        // destroy socket
        if (keepAlive === false) {
          ctx.response.set('Connection', 'close')
        }
        switch (url) {
          case '/restart':
            ctx.body = 'success'
            process.send('restart')
            break;
          default:
            ctx.body = 'world-----' + Date.now()
        }
      })
      const server = app.listen(9011)
      
      process.on('message', (data, sendHandle) => {
        if (data == 'stop') {
          keepAlive = false
          server.close();
        }
      })

      【讨论】:

        【解决方案9】:

        我已经在不同的 支持频道上多次回答了“如何终止 HTTP 服务器”的变体。不幸的是,我不能推荐任何现有的库,因为它们以一种或另一种方式是lacking。从那以后,我整理了一个包,(我相信)它可以处理所有预期的优雅 HTTP 服务器终止的情况。

        https://github.com/gajus/http-terminator

        http-terminator 的主要好处是:

        • 它不会对 Node.js API 进行猴子补丁
        • 它会立即销毁所有没有附加 HTTP 请求的套接字
        • 它允许对正在进行的 HTTP 请求的套接字进行优雅的超时
        • 它可以正确处理 HTTPS 连接
        • 它通过设置连接来通知使用 keep-alive 的连接服务器正在关闭:关闭标头
        • 它不会终止 Node.js 进程

        用法:

        import http from 'http';
        import {
          createHttpTerminator,
        } from 'http-terminator';
        
        const server = http.createServer();
        
        const httpTerminator = createHttpTerminator({
          server,
        });
        
        await httpTerminator.terminate();
        
        

        【讨论】:

          【解决方案10】:

          process.exit(代码); // 代码 0 表示成功,1 表示失败

          【讨论】:

          • 问题说process.exit()不是一个选项。
          猜你喜欢
          • 1970-01-01
          • 2011-07-12
          • 2020-08-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多