Как мне немедленно отключить http(s) сервер Node.js?

У меня есть приложение Node.js, которое содержит сервер http(s).

В конкретном случае мне нужно отключить этот сервер программно. То, что я сейчас делаю, зовет его close() функция, но это не помогает, так как она ждет, пока все оставшиеся в живых соединения завершатся первыми.

Таким образом, в основном, это отключает сервер, но только после минимального времени ожидания 120 секунд. Но я хочу, чтобы сервер немедленно отключился - даже если это означает разрыв с обрабатываемыми в данный момент запросами.

Что я не могу сделать, это просто

process.exit();

поскольку сервер является только частью приложения, а остальная часть приложения должна оставаться запущенной. То, что я ищу, это концептуально что-то вроде server.destroy(); или что-то типа того.

Как я мог этого добиться?

PS: Тайм-аут keep-alive для подключений обычно требуется, следовательно, сократить этот промежуток времени невозможно.

11 ответов

Решение

Хитрость в том, что вам нужно подписаться на сервер 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 секунд. Если соединение не установлено, сервер немедленно отключается.

Если соединение установлено и оно все еще открыто, оно уничтожается. Если он уже умер естественным образом, в этот момент распечатывается только сообщение.

Я нашел способ сделать это без необходимости отслеживать соединения или заставлять их закрываться. Я не уверен, насколько он надежен в версиях Node или есть какие-либо негативные последствия для этого, но, похоже, он отлично работает для того, что я делаю. Хитрость заключается в том, чтобы генерировать "закрытое" событие, используя setImmediate сразу после вызова close метод. Это работает так:

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

По крайней мере, для меня это приводит к освобождению порта, чтобы я мог запустить новую службу HTTP(S) к тому времени, когда вызывается обратный вызов (что происходит практически мгновенно). Существующие соединения остаются открытыми. Я использую это для автоматического перезапуска службы HTTPS после обновления сертификата Let's Encrypt.

Если вам необходимо сохранить процесс после закрытия сервера, то решение 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) чтобы закончить ваши изящные процедуры выключения.

Библиотека https://github.com/isaacs/server-destroy предоставляет простой способ destroy() сервер с желаемым поведением в вопросе (отслеживая открытые соединения и уничтожая каждое из них при уничтожении сервера, как описано в других ответах).

Как уже говорили другие, решение состоит в том, чтобы отслеживать все открытые розетки и закрывать их вручную. Мой пакет узлов killable может сделать это для вас. Пример (с использованием Express, но вы можете вызвать использование Killable на любой http.server пример):

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

Еще один пакет nodejs, который выполняет отключение, убивающее соединения: http-shutdown, который на момент написания (сентябрь 2016 г.), по всей видимости, поддерживался и работал для меня на 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.');
});

Моим лучшим предположением будет уничтожение соединений вручную (то есть принудительное закрытие сокетов).

В идеале, это должно быть сделано, копаясь во внутренностях сервера и закрывая его сокеты вручную. В качестве альтернативы, можно запустить команду оболочки, которая делает то же самое (при условии, что сервер имеет надлежащие привилегии и т. Д.)

Я ответил на изменение " как оканчивается сервер HTTP" много раз на разном Node.js поддержку каналов. К сожалению, я не могу рекомендовать какой - либо из существующих библиотек, поскольку они отсутствуют в том или ином пути. С тех пор я собрал пакет, который (как мне кажется) обрабатывает все случаи, ожидаемые от корректного завершения работы HTTP-сервера.

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

Основное преимущество http-terminator заключается в том, что:

  • это не обезьяна-патч Node.js API
  • он немедленно уничтожает все сокеты без прикрепленного HTTP-запроса
  • он позволяет плавно отключать сокеты с текущими HTTP-запросами
  • он правильно обрабатывает HTTPS-соединения
  • он сообщает соединениям, использующим keep-alive, что сервер выключается, устанавливая заголовок connection: close
  • он не завершает процесс Node.js

Применение:

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

const server = http.createServer();

const httpTerminator = createHttpTerminator({
  server,
});

await httpTerminator.terminate();

Наконец, у нас есть функция server.closeAllConnections() в версии 18.2.0.

Вы можете просто вызвать эту функцию раньше.

Кроме того, если вы не хотите закрывать активные соединения и хотите их дождаться, перед этим вызовите функцию server.closeIdleConnections().server.close().

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

process.exit(код); // код 0 для успеха и 1 для неудачи

Другие вопросы по тегам