Node js - http.request() проблемы с подключением пула
Рассмотрим следующее простое приложение Node.js:
var http = require('http');
http.createServer(function() { }).listen(8124); // Prevent process shutting down
var requestNo = 1;
var maxRequests = 2000;
function requestTest() {
http.request({ host: 'www.google.com', method: 'GET' }, function(res) {
console.log('Completed ' + (requestNo++));
if (requestNo <= maxRequests) {
requestTest();
}
}).end();
}
requestTest();
Он делает 2000 HTTP-запросов на google.com один за другим. Проблема заключается в том, что он запрашивает номер 5 и приостанавливается примерно на 3 минуты, затем продолжает обработку запросов 6 - 10, затем останавливается еще на 3 минуты, затем запрашивает 11 - 15, паузы и т.д. Изменить: Я попытался сменить www.google.com на localhost, чрезвычайно простое приложение Node.js, на котором запущена моя машина, которая возвращает "Hello world", я все равно получаю 3-минутную паузу.
Теперь я читаю, что могу увеличить ограничение пула соединений:
http.globalAgent.maxSockets = 20;
Теперь, если я запустил его, он обрабатывает запросы 1 - 20, затем останавливается на 3 минуты, затем запрашивает 21 - 40, затем паузы и т.д.
Наконец, после небольшого исследования я узнал, что могу полностью отключить объединение пулов, установив agent: false
в параметры запроса:
http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) {
...snip....
... и он будет работать через все запросы 2000 только отлично.
Мой вопрос, это хорошая идея? Есть ли опасность, что я могу получить слишком много HTTP-соединений? И почему он останавливается на 3 минуты, конечно, если я закончил с соединением, он должен добавить его прямо в бассейн, готовый к следующему запросу, чтобы использовать, так почему он ждет 3 минуты? Простите мое невежество.
В противном случае, что является лучшей стратегией для приложения Node.js, создающего потенциально большое количество HTTP-запросов без блокировки или сбоя?
Я запускаю Node.js версию 0.10 на Mac OSX 10.8.2.
Изменить:. Я обнаружил, что если я конвертирую вышеуказанный код в цикл for и пытаюсь установить связку соединений одновременно, я начинаю получать ошибки после примерно 242 подключений. Ошибка:
Error was thrown: connect EMFILE
(libuv) Failed to create kqueue (24)
... и код...
for (var i = 1; i <= 2000; i++) {
(function(requestNo) {
var request = http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) {
console.log('Completed ' + requestNo);
});
request.on('error', function(e) {
console.log(e.name + ' was thrown: ' + e.message);
});
request.end();
})(i);
}
Я не знаю, сможет ли сильно загруженное приложение Node.js достичь такого количества одновременных подключений.
Ответы
Ответ 1
Вы должны использовать ответ.
Помните, что в v0.10 мы приземлились потоки2. Это означает, что события data
не происходят, пока вы не начнете искать их. Итак, вы можете делать такие вещи:
http.createServer(function(req, res) {
// this does some I/O, async
// in 0.8, you'd lose data chunks, or even the 'end' event!
lookUpSessionInDb(req, function(er, session) {
if (er) {
res.statusCode = 500;
res.end("oopsie");
} else {
// no data lost
req.on('data', handleUpload);
// end event didn't fire while we were looking it up
req.on('end', function() {
res.end('ok, got your stuff');
});
}
});
});
Однако обратная сторона потоков, которые не теряют данные, когда вы ее не читаете, заключается в том, что они на самом деле не теряют данные, если вы их не читаете! То есть, они начинаются с паузы, и вы должны прочитать их, чтобы получить что-то.
Итак, что происходит в вашем тесте, это то, что вы делаете кучу запросов и не потребляете ответы, а затем сокет становится убитым google, потому что ничего не происходит, и предполагается, что вы умерли.
Есть случаи, когда невозможно потреблять входящее сообщение: то есть, если вы не добавляете обработчик событий response
в запросах или где вы полностью пишете и завершаете сообщение response
на сервере без чтения запроса. В этих случаях мы просто выгружаем данные в мусор для вас.
Однако, если вы слушаете событие 'response'
, это ваша ответственность за обработку объекта. Добавьте в свой первый пример response.resume()
, и вы увидите, что процесс проходит через разумные темпы.