63

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

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

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

我不能做的是一个简单的

process.exit();

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

我怎么能做到这一点?

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

4

10 回答 10

78

诀窍是您需要订阅服务器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 秒后关闭服务器。如果没有建立连接,服务器会立即关闭。

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

于 2013-01-31T22:52:20.320 回答
23

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

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

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

于 2016-04-24T23:27:24.487 回答
13

如果您需要在关闭服务器后保持进程处于活动状态,那么 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)以完成您的优雅关闭过程。

于 2015-07-19T19:05:24.493 回答
8

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

于 2015-01-13T18:37:12.043 回答
6

正如其他人所说,解决方案是跟踪所有打开的套接字并手动关闭它们。我的 node package 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);
于 2014-12-21T16:26:44.710 回答
3

另一个执行关闭终止连接的 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.');
});
于 2016-09-29T11:34:17.800 回答
2

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

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

于 2013-01-31T14:01:17.990 回答
0

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

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

http-terminator的主要好处是:

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

用法:

import http from 'http';
import {
  createHttpTerminator,
} from 'http-terminator';

const server = http.createServer();

const httpTerminator = createHttpTerminator({
  server,
});

await httpTerminator.terminate();

于 2020-01-20T02:45:01.197 回答
0

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();
  }
})

于 2019-04-26T07:56:19.740 回答
-5

进程.退出(代码);// 代码 0 表示成功,1 表示失败

于 2016-05-17T19:38:15.280 回答