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(), и вы увидите, что процесс проходит через разумные темпы.