Принудительное закрытие всех подключений на http-сервере node.js
У меня есть http-сервер, созданный с помощью:
var server = http.createServer()
Я хочу закрыть сервер. Предположительно, я сделал бы это, позвонив:
server.close()
Однако это только предотвращает получение сервером новых HTTP-соединений. Он не закрывает все, что еще открыто. http.close()
выполняет обратный вызов, и этот обратный вызов не выполняется, пока все открытые соединения фактически не отключены. Есть ли способ заставить все закрыть?
Корень проблемы для меня заключается в том, что у меня есть тесты Mocha, которые запускают HTTP-сервер в их настройке (beforeEach()
), а затем закрывают его в режиме teardown (afterEach()
). Но так как просто вызов server.close()
не будет полностью закрывать все, последующий http.createServer()
часто приводит к ошибке EADDRINUSE
. Ожидание close()
для завершения также не является опцией, поскольку открытые соединения могут занимать очень много времени.
Мне нужен способ принудительно закрыть соединения. Я могу сделать это на стороне клиента, но заставляю все мои тестовые подключения закрываться, но я предпочел бы сделать это на стороне сервера, то есть просто сообщить http-серверу о том, чтобы закрыть все сокеты.
Ответы
Ответ 1
Вам нужно
- подписаться на событие
connection
сервера и добавить открытые сокеты в массив
- отслеживать открытые сокеты, подписываясь на их событие
close
и удаляя закрытые из вашего массива
- вызывать
destroy
во всех остальных открытых сокетах, когда вам нужно завершить работу сервера
У вас также есть возможность запустить сервер в дочернем процессе и выйти из этого процесса, когда вам нужно.
Ответ 2
Для справки для других, которые спотыкаются по этому вопросу, библиотека https://github.com/isaacs/server-destroy обеспечивает простой способ destroy()
сервера (используя подход, описанный Ege).
Ответ 3
Обычно я использую нечто похожее на это:
var express = require('express');
var server = express();
/* a dummy route */
server.get('/', function (req, res) {
res.send('Hello World!');
});
/* handle SIGTERM and SIGINT (ctrl-c) nicely */
process.once('SIGTERM', end);
process.once('SIGINT', end);
var listener = server.listen(8000, function(err) {
if (err) throw err;
var host = listener.address().address;
var port = listener.address().port;
console.log('Server listening at http://%s:%s', host, port);
});
var lastSocketKey = 0;
var socketMap = {};
listener.on('connection', function(socket) {
/* generate a new, unique socket-key */
var socketKey = ++lastSocketKey;
/* add socket when it is connected */
socketMap[socketKey] = socket;
socket.on('close', function() {
/* remove socket when it is closed */
delete socketMap[socketKey];
});
});
function end() {
/* loop through all sockets and destroy them */
Object.keys(socketMap).forEach(function(socketKey){
socketMap[socketKey].destroy();
});
/* after all the sockets are destroyed, we may close the server! */
listener.close(function(err){
if(err) throw err();
console.log('Server stopped');
/* exit gracefully */
process.exit(0);
});
}
он, как и Ege Özcan, просто собирает сокеты в событии соединения и закрывает сервер, уничтожая их.
Ответ 4
Мой подход исходит от этого и в основном делает то, что сказал @Ege Özcan.
Единственное дополнение - установить маршрут для отключения моего сервера, потому что node не получал сигналы от моего терминала ('SIGTERM'
и 'SIGINT'
).
Ну, node получал сигналы от моего терминала при выполнении node whatever.js
, но при делегировании этой задачи на script (например, 'start'
script в package.json → npm start
) его не удалось отключить на Ctrl+C
, поэтому этот подход сработал у меня.
Обратите внимание, что я под Cygwin и для меня убил сервер, прежде чем это означало закрыть терминал и снова открыть его снова.
Также обратите внимание, что я использую express для материала маршрутизации.
var http=require('http');
var express= require('express');
var app= express();
app.get('/', function (req, res) {
res.send('I am alive but if you want to kill me just go to <a href="/exit">/exit</a>');
});
app.get('/exit', killserver);
var server =http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000');
/*console.log(process);*/
});
// 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);
});
// close the server and destroy all the open sockets
function killserver() {
console.log("U killed me but I'll take my revenge soon!!");
// 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();
}
};