Node.js Http.request тормозит при нагрузочном тестировании. Я делаю что-то неправильно?

Вот мой пример кода:

var http = require('http');

var options1 = {
          host: 'www.google.com',
          port: 80,
          path: '/',
          method: 'GET'
        };

http.createServer(function (req, res) {

        var start = new Date();
        var myCounter = req.query['myCounter'] || 0;

        var isSent = false;
        http.request(options1, function(response) {
            response.setEncoding('utf8');
            response.on('data', function (chunk) {
                var end = new Date();
                console.log(myCounter + ' BODY: ' + chunk  + " time: " + (end-start) + " Request start time: " + start.getTime());

                if (! isSent) {
                    isSent = true;
                    res.writeHead(200, {'Content-Type': 'application/xml'});
                    res.end(chunk);
                }
            });
        }).end();


}).listen(3013);

console.log('Server running at port 3013');

Я обнаружил, что если я подключусь к другому серверу (Google или любому другому), ответ будет все медленнее и медленнее до нескольких секунд. Этого не произойдет, если я подключусь к другому серверу node.js в той же сети.

Я использую JMeter для тестирования. 50 одновременных операций в секунду с 1000 циклом.

Понятия не имею, в чем проблема ...

=========================

Дальнейшее расследование:

Я запускаю тот же сценарий на Rackspace, а также на EC2 для тестирования. И скрипт будет использовать http.request для подключения к: Google, Facebook, а также моему другому скрипту, который просто выводит данные (например, hello world), которые размещены на другом экземпляре EC2.

Инструмент для тестирования - это jMeter на моем рабочем столе.

Предварительный тест node.js: jMeter -> Google Результат: быстро и стабильно. jMeter -> Facebook результат: быстро и стабильно. jMeter -> My Simple Output Script Результат: быстрый и последовательный.

Затем я делаю 50 параллельных потоков в секунду, со 100 циклами, тестируя мои Rackspace nodejs, а затем EC2 node.js, у которого такая же проблема производительности jMeter -> node.js -> Google Результат: от 50 мс до 2000 мс на 200 запросов.
jMeter -> node.js -> Facebook Результат: от 200 мс до 3000 мс после 200 запросов.
jMeter -> node.js -> Мой простой сценарий вывода Результат: от 100 мс переходит в 1000 мс после 200 запросов.
Первые 10-20 запросов выполняются быстро, затем начинают замедляться.

Затем, когда я перехожу на 10 одновременных потоков, все начинает меняться ... Ответ очень последовательный, без замедления.

Что-то, связанное с количеством параллельных потоков, которые может обрабатывать Node.js (http.request).

------------ Подробнее --------------

Сегодня я провел еще один тест, и вот он: я использовал http.Agent и увеличил максимальное количество сокетов. Однако интересно то, что на одном тестовом сервере (EC2) он значительно улучшается и больше не замедляется. Однако другой сервер (место в стойке) только немного улучшается. Он по-прежнему показывает замедление. Я даже установил "Connection: close" в заголовке запроса, это улучшает только 100 мс.

если http.request использует пул соединений, как его увеличить?

на обоих серверах, если я использую "ulimit -a", номер открытого файла будет 1024.

------------- ** БОЛЬШЕ И БОЛЬШЕ ** -------------------

Кажется, что даже я установил maxSockets на большее число, он работает только с некоторым пределом. Кажется, существует внутреннее или зависящее от ОС ограничение сокета. Однако поднять его?

------------- ** ПОСЛЕ ОБЩИХ ИСПЫТАНИЙ ** ---------------

Прочитав много сообщений, я узнал:



цитата из: https://github.com/joyent/node/issues/877


1) Если я установил заголовки с помощью connection = 'keep-alive', производительность будет хорошей и может увеличиться до maxSocket = 1024 (что является моей настройкой в ​​Linux).

var options1 = {
                  host: 'www.google.com',
                  port: 80,
                  path: '/',
                  method: 'GET',
    **headers: {
            'Connection':'keep-alive'
    }**
                };

Если бы я установил его на «Соединение»: «закрыть», время отклика было бы в 100 раз медленнее.

Здесь происходили забавные вещи:

1) в EC2, когда я впервые тестирую Connection: keep-alive, это займет около 20-30 мс. Затем, если я перейду на «Соединение: закрыть» ИЛИ установлю «Агент: ложь», время отклика снизится до 300 мс. WIHTOUT перезапускает сервер, если я снова перейду на Connection: keep-alive, время отклика замедлится еще больше до 4000 мс. Либо мне нужно перезапустить сервер, либо подождать некоторое время, чтобы вернуть свою скорость освещения 20-30 мс.

2) Если я запустил его с агентом: false, сначала время отклика уменьшится до 300 мс. Но затем он снова станет быстрее и снова станет «нормальным».

Я предполагаю, что пул соединений все еще действует, даже если вы установили agent: false. Однако, если вы сохраните соединение: keep-alive, то это точно будет быстро. просто не переключай его.




Обновление от 25 июля 2011 г.

Я попробовал последнюю версию node.js V0.4.9 с исправлением http.js и https.js из https://github.com/mikeal/node/tree/http2

производительность намного лучше и стабильнее.


person murvinlai    schedule 01.06.2011    source источник
comment
Вместо того, чтобы делать это isSent волшебство, вы должны прислушиваться к response.on('end') событию.   -  person Daniel Baulig    schedule 02.06.2011
comment
Я сталкиваюсь с той же проблемой и вижу значительный прирост производительности с изменением проверки активности, однако через некоторое время при нагрузочном тестировании я сталкиваюсь с ошибкой. Ниже приведена ошибка: {[Ошибка: подключите EADDRNOTAVAIL] код: 'EADDRNOTAVAIL', номер ошибки: 'EADDRNOTAVAIL', системный вызов: 'connect'}   -  person firemonkey    schedule 30.04.2013
comment
Ссылка в этом комментарии теперь дает ошибку 404.   -  person bean5    schedule 02.09.2015


Ответы (4)


Я решил проблему с

require('http').globalAgent.maxSockets = 100000

or

agent = new http.Agent()
agent.maxSockets = 1000000  # 1 million
http.request({agent:agent})
person haijin    schedule 25.10.2012

Я боролся с той же проблемой. Я обнаружил, что моим узким местом является DNS, хотя я не совсем понимаю, где и почему. Если я сделаю свои запросы к чему-то вроде http://myserver.com/asd, я с трудом смогу запустить 50- 100 rq / s, и если я выйду за пределы 100 rq / s и другие вещи станут катастрофой, время отклика станет огромным, и некоторые запросы никогда не закончатся и ждут бесконечно, и мне нужно убить -9 мой сервер. Если я сделаю запросы на IP-адрес сервера, все будет стабильно со скоростью 500 rq / s, хотя и не совсем гладко, и график (у меня есть график в реальном времени) будет пиковым. И будьте осторожны, в Linux все еще есть ограничение на количество открытых файлов, и мне удалось его поразить. Другое наблюдение заключается в том, что процесс с одним узлом не может плавно составлять 500 рк / с. Но я могу запустить 4 процесса узла, каждый со скоростью 200 оборотов в секунду, и я получаю очень плавный график, постоянную загрузку процессора / сети и очень короткое время отклика. Это узел 0.10.22.

person bobef    schedule 26.11.2013
comment
Оказалось, что это ошибка nodejs с DNS. Я не могу сказать наверняка, но из моей проверки кода кажется, что есть случай, когда обратный вызов с разрешением DNS может быть назначен после события, но я перестал копать глубже после того, как обнаружил, что в нестабильной версии узла произошли изменения в этом направлении. и там проблема не проявляется. Кажется, исправлено в узле 0.11.9, и моя проблема с бесконечным запросом там не проявляется. - person bobef; 29.11.2013

Это не обязательно решит вашу проблему, но немного очистит ваш код и использует различные события так, как вам следует:

var http = require('http');

var options1 = {
      host: 'www.google.com',
      port: 80,
      path: '/',
      method: 'GET'
};

http.createServer(function (req, res) {
    var start = new Date();
    var myCounter = req.query['myCounter'] || 0;

    http.request(options1, function(response) {
        res.on('drain', function () { // when output stream's buffer drained 
            response.resume(); // continue to receive from input stream
        });
        response.setEncoding('utf8');
        res.writeHead(response.statusCode, {'Content-Type': 'application/xml'});
        response.on('data', function (chunk) {
            if (!res.write(chunk)) { // if write failed, the stream is choking
                response.pause(); // tell the incoming stream to wait until output stream drained
            }
        }).on('end', function () {
            var end = new Date();
            console.log(myCounter + ' time: ' + (end-start) + " Request start time: " + start.getTime());
            res.end();
        });
    }).end();
}).listen(3013);

console.log('Server running at port 3013');

Снял вывод тела. Поскольку мы осуществляем потоковую передачу из одного сокета в другой, мы не можем быть уверены, что увидим все тело в любое время без его буферизации.

РЕДАКТИРОВАТЬ: Я считаю, что узел использует пул соединений для http.request. Если у вас 50 одновременных подключений (и, следовательно, 50 одновременных попыток http.request), возможно, вы работаете с ограничением пула подключений. В настоящее время у меня нет времени искать это для вас, но вы должны взглянуть на документацию узла, касающуюся http, особенно http agent.

ИЗМЕНИТЬ 2: есть ветка по поводу очень похожей проблемы в списке рассылки node.js. Вам стоит взглянуть на него, особенно пост Микаэля должен заинтересовать. Он предлагает полностью отключить пул соединений для запросов, передав параметр agent: false в вызов http.request. У меня нет никаких подсказок, поэтому, если это не поможет, возможно, вам стоит попытаться получить помощь в списке рассылки node.js.

person Daniel Baulig    schedule 02.06.2011
comment
все то же самое. Я работал на сервере Rackspace, и теперь я попробую это на сервере EC2 и посмотрю, имеет ли это какое-либо значение. Черт... :( - person murvinlai; 03.06.2011
comment
Привет. Сделал сегодня тест. Пожалуйста, посмотрите мое обновление в моем исходном сообщении выше. :) - person murvinlai; 03.06.2011
comment
Смотрите, пожалуйста, мою вторую правку. У меня нет другого представления, почему существует разница в поведении EC2 и Rackspace. Может это из-за ограничений пропускной способности? - person Daniel Baulig; 03.06.2011
comment
Я просто провожу тест. агент установки: false не влияет. Ограничивается ли пул соединений аппаратным обеспечением или настройками ОС? - person murvinlai; 04.06.2011
comment
@Daniel, возможно, вы столкнулись с чем-то с максимальным количеством подключений агента, по умолчанию оно равно пяти, и я не вижу приведенного выше кода, который меняет это для текущего сервера (ов) https://github.com/joyent/node/blob/master/lib/http.js#L1139 - person jcolebrand; 06.06.2011

Проблема Github 877 может быть связана с:

https://github.com/joyent/node/issues/877

Хотя мне непонятно, попадаете ли вы в это дело. Когда я нажал на это, у меня сработало обходное решение «agent: false», как и установка заголовка «connection: keep-alive» с запросом.

person Dave Pacheco    schedule 06.06.2011
comment
Я обнаружил, что если установить agent: false с connection: keep-alive, скорость будет постоянной, но немного медленнее. Если агент настроен на соединение: keep-alive, иногда первая пара запросов будет медленнее, но тогда это будет очень быстро. (быстрее, чем агент: ложь). если соединение не установлено или соединение: закрыто, то это будет катастрофа. - person murvinlai; 07.06.2011