web-dev-qa-db-fra.com

Comment puis-je arrêter un serveur Node.js http immédiatement?

J'ai une application Node.js qui contient un serveur http (s).

Dans un cas spécifique, j'ai besoin d'arrêter ce serveur par programme. Ce que je fais actuellement, c'est appeler sa fonction close(), mais cela n'aide pas, car il attend que toutes les connexions maintenues en vie se terminent en premier.

Donc, en gros, cela arrête le serveur, mais seulement après un temps d'attente minimum de 120 secondes. Mais je veux que le serveur s'arrête immédiatement - même si cela signifie rompre avec les demandes actuellement traitées.

Ce que je ne peux pas faire est simple

process.exit();

car le serveur n'est qu'une partie de l'application et le reste de l'application doit rester en cours d'exécution. Ce que je recherche, c'est conceptuellement quelque chose comme server.destroy(); ou quelque chose comme ça.

Comment pourrais-je y parvenir?

PS: le délai d'expiration pour les connexions est généralement requis, il n'est donc pas une option viable pour diminuer ce temps.

50
Golo Roden

L'astuce est que vous devez vous abonner à l'événement connection du serveur qui vous donne le socket de la nouvelle connexion. Vous devez vous souvenir de cette socket et plus tard, directement après avoir appelé server.close(), détruisez cette socket à l'aide de socket.destroy().

De plus, vous devez écouter l'événement close du socket pour le supprimer du tableau s'il part naturellement car son délai d'expiration est épuisé.

J'ai écrit un petit exemple d'application que vous pouvez utiliser pour démontrer ce comportement:

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

Fondamentalement, ce qu'il fait est de démarrer un nouveau serveur HTTP, de compter de 10 à 0 et de fermer le serveur après 10 secondes. Si aucune connexion n'a été établie, le serveur s'arrête immédiatement.

Si une connexion a été établie et qu'elle est toujours ouverte, elle est détruite. S'il était déjà mort naturellement, seul un message est imprimé à ce moment-là.

65
Golo Roden

J'ai trouvé un moyen de le faire sans avoir à garder une trace des connexions ou à les forcer à se fermer. Je ne sais pas à quel point il est fiable dans les versions Node ou s'il y a des conséquences négatives à cela, mais cela semble fonctionner parfaitement pour ce que je fais. L'astuce consiste à émettre le événement "close" utilisant setImmediate juste après avoir appelé la méthode close. Cela fonctionne comme ceci:

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

Au moins pour moi, cela finit par libérer le port afin que je puisse démarrer un nouveau service HTTP (S) au moment où le rappel est appelé (ce qui est à peu près instantané). Les connexions existantes restent ouvertes. J'utilise ceci pour redémarrer automatiquement le service HTTPS après avoir renouvelé un certificat Let's Encrypt.

17
Jonathan Gray

Si vous devez maintenir le processus en vie après la fermeture du serveur, la solution de Golo Roden est probablement la meilleure.

Mais si vous fermez le serveur dans le cadre d'un arrêt progressif du processus, vous avez juste besoin de ceci:

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

Fondamentalement, les sockets que votre serveur utilise ne maintiendront le processus en vie que pendant le traitement d'une demande. Alors qu'ils sont juste assis paresseusement (en raison d'une connexion Keep-Alive), un appel à server.close() fermera le processus, tant qu'il n'y a rien d'autre qui le maintient en vie. Si vous devez effectuer d'autres tâches après la fermeture du serveur, dans le cadre de votre arrêt progressif, vous pouvez vous connecter à process.on('beforeExit', callback) pour terminer vos procédures d'arrêt progressif.

8
Joshua Wise

La bibliothèque https://github.com/isaacs/server-destroy fournit un moyen simple de destroy() un serveur avec le comportement souhaité dans la question (en suivant les connexions ouvertes et en détruisant chacun d'eux sur le serveur détruit, comme décrit dans d'autres réponses).

7
ploer

Comme d'autres l'ont dit, la solution est de garder une trace de toutes les sockets ouvertes et de les fermer manuellement. Mon package de nœuds killable peut le faire pour vous. Un exemple (en utilisant express, mais vous pouvez appeler use killable sur n'importe quel http.server exemple):

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);
4
marten-de-vries

Encore un autre package nodejs pour effectuer un arrêt tuant les connexions: http-shutdown , qui semble raisonnablement maintenu au moment de la rédaction (septembre 2016) et a fonctionné pour moi sur NodeJS 6.x

De la documentation

Utilisation

Il existe actuellement deux façons d'utiliser cette bibliothèque. Le premier est l'habillage explicite de l'objet 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.');
});

Le second ajoute implicitement une fonctionnalité prototype à l'objet Serveur:

// .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.');
});
3
Bruno Grieder

Ma meilleure supposition serait de tuer les connexions manuellement (c'est-à-dire de fermer de force ses sockets).

Idéalement, cela devrait être fait en creusant dans les internes du serveur et en fermant ses prises à la main. Alternativement, on pourrait exécuter une commande Shell qui fait de même (à condition que le serveur dispose des privilèges appropriés, etc.)

2
Morten Siebuhr
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();
  }
})
0
vapour