Почему этот блок JavaScript в Node.js? - PullRequest
6 голосов
/ 17 августа 2011

У меня есть следующий простой http-сервер, использующий Node.js:

var http = require('http');

var server = http.createServer(function(req, res) {
    var counter = 0;

    for(var i = 1; i <= 30; i++) {
        http.get({ host: "www.google.com" }, function(r) {
            counter++;
            res.write("Response " + counter + ": " + r.statusCode + "\n");
            if(counter == 30) res.end();                                                                                                                                   
        });
    }
});

server.listen(8000);

Когда я вхожу в свой локальный хост на порту 8000, я получаю ожидаемый результат:

Response 1: 200
Response 2: 200
Response 3: 200
...
Response 30: 200

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

Насколько я понимаю, поскольку это асинхронный код, использующий обратные вызовы, этот узел может обрабатывать несколько запросов синхронно, обрабатывая их на следующем такте цикла обработки событий. На самом деле, я даже смотрел видео о том, как Райан Даль делает что-то похожее с примером «Привет, мир!». Что в моем коде делает блок сервера?

Ответы [ 3 ]

8 голосов
/ 17 августа 2011

Ваша проблема не имеет ничего общего с блокировкой вызовов;это связано с тем фактом, что вы можете одновременно открывать только определенное количество подключений к одному хосту.Как только вы достигнете максимального количества открытых соединений, другие асинхронные вызовы на http.get должны ждать, пока количество открытых соединений снова не уменьшится, что происходит, когда другие запросы завершены и их обратные вызовы запущены.Поскольку вы создаете новые запросы быстрее, чем они истощают, вы получаете, казалось бы, блокирующие результаты.

Вот модифицированная версия вашей программы, которую я создал для проверки этого.(Обратите внимание, что существует более простой способ решения вашей проблемы, как указано в mtomis - подробнее об этом ниже.) Я добавил некоторые записи console.log, чтобы было проще определить, в каком порядке обрабатывались объекты;Я также отклоняю все запросы на что-либо, кроме /, так что favicon.ico запросы игнорируются.Наконец, я делаю запросы ко многим различным веб-сайтам.

var http = require('http');

// http://mostpopularwebsites.net/1-50/
var sites = [
  "www.google.com", "www.facebook.com", "www.youtube.com",
  "www.yahoo.com", "www.blogspot.com", "www.baidu.com", "www.live.com",
  "www.wikipedia.org", "www.twitter.com", "www.qq.com", "www.msn.com",
  "www.yahoo.co.jp", "www.sina.com.cn", "www.google.co.in", "www.taobao.com",
  "www.amazon.com", "www.linkedin.com", "www.google.com.hk",
  "www.wordpress.com", "www.google.de", "www.bing.com", "www.google.co.uk",
  "www.yandex.ru", "www.ebay.com", "www.google.co.jp", "www.microsoft.com",
  "www.google.fr", "www.163.com", "www.google.com.br",
  "www.googleusercontent.com", "www.flickr.com"
];

var server = http.createServer(function(req, res) {
  console.log("Got a connection.");
  if(req.url != "/") {
    console.log("But returning because the path was not '/'");
    res.end();
    return;
  }

  var counter = 0;

  for(var i = 1; i <= 30; i++) {
    http.get({ host: sites[i] }, function(index, host, r) {
      counter++;
      console.log("Response " + counter + " from # " + index + " (" + host + ")");
      res.write("Response " + counter + " from # " + index + " (" + host + ")\n");
      if(counter == 30) res.end();
    }.bind(this, i, sites[i]));
  }
  console.log("Done with for loop.");
});

server.listen(8000);

Я запустил эту программу и очень быстро посетил страницу в двух разных браузерах (я также очистил свой DNS-кэш, так как тест выполнялся слишком быстро, чтобы получитьхороший выход в противном случае).Вот вывод:

Got a connection.
Done with for loop.
Response 1 from # 8 (www.twitter.com)
Response 2 from # 1 (www.facebook.com)
Response 3 from # 12 (www.sina.com.cn)
Response 4 from # 4 (www.blogspot.com)
Response 5 from # 13 (www.google.co.in)
Response 6 from # 19 (www.google.de)
Response 7 from # 26 (www.google.fr)
Response 8 from # 28 (www.google.com.br)
Response 9 from # 17 (www.google.com.hk)
Response 10 from # 6 (www.live.com)
Response 11 from # 20 (www.bing.com)
Response 12 from # 29 (www.googleusercontent.com)
Got a connection.
Done with for loop.
Response 13 from # 10 (www.msn.com)
Response 14 from # 2 (www.youtube.com)
Response 15 from # 18 (www.wordpress.com)
Response 16 from # 16 (www.linkedin.com)
Response 17 from # 7 (www.wikipedia.org)
Response 18 from # 3 (www.yahoo.com)
Response 19 from # 15 (www.amazon.com)
Response 1 from # 6 (www.live.com)
Response 2 from # 1 (www.facebook.com)
Response 3 from # 8 (www.twitter.com)
Response 4 from # 4 (www.blogspot.com)
Response 20 from # 11 (www.yahoo.co.jp)
Response 21 from # 9 (www.qq.com)
Response 5 from # 2 (www.youtube.com)
Response 6 from # 13 (www.google.co.in)
Response 7 from # 10 (www.msn.com)
Response 8 from # 24 (www.google.co.jp)
Response 9 from # 17 (www.google.com.hk)
Response 10 from # 18 (www.wordpress.com)
Response 11 from # 16 (www.linkedin.com)
Response 12 from # 3 (www.yahoo.com)
Response 13 from # 12 (www.sina.com.cn)
Response 14 from # 11 (www.yahoo.co.jp)
Response 15 from # 7 (www.wikipedia.org)
Response 16 from # 15 (www.amazon.com)
Response 17 from # 9 (www.qq.com)
Response 22 from # 5 (www.baidu.com)
Response 23 from # 27 (www.163.com)
Response 24 from # 14 (www.taobao.com)
Response 18 from # 5 (www.baidu.com)
Response 19 from # 14 (www.taobao.com)
Response 25 from # 24 (www.google.co.jp)
Response 26 from # 30 (www.flickr.com)
Response 20 from # 29 (www.googleusercontent.com)
Response 21 from # 22 (www.yandex.ru)
Response 27 from # 23 (www.ebay.com)
Response 22 from # 19 (www.google.de)
Response 23 from # 21 (www.google.co.uk)
Response 24 from # 28 (www.google.com.br)
Response 25 from # 25 (www.microsoft.com)
Response 26 from # 20 (www.bing.com)
Response 27 from # 30 (www.flickr.com)
Response 28 from # 22 (www.yandex.ru)
Response 28 from # 27 (www.163.com)
Response 29 from # 25 (www.microsoft.com)
Response 29 from # 26 (www.google.fr)
Response 30 from # 21 (www.google.co.uk)
Response 30 from # 23 (www.ebay.com)
Got a connection.
But returning because the path was not '/'

Как вы можете видеть, кроме периода времени, который потребовался мне, чтобы нажать Alt+Tab Enter, обратные вызовы полностью перемешаны - асинхронный, неблокирующий ввод / вывод присамое лучшее.

[Edit]

Как упоминалось в mtomis, максимальное количество подключений, которое вы можете открыть на хост, настраивается через глобальный http.globalAgent.maxSockets.Просто установите для этого числа одновременных подключений, которое вы хотите обрабатывать для каждого хоста, и проблема, с которой вы столкнулись, исчезнет.

5 голосов
/ 17 августа 2011

Node.js имеет ограничение на количество клиентских подключений на хост (по умолчанию 5 подключений на хост), как описано здесь: http://nodejs.org/docs/v0.5.4/api/http.html#agent.maxSockets

Причина, по которой ваш второй процесс curl зависает до завершения первого, заключается в том, что первый процесс ставит в очередь 30 запросов, 5 из которых могут быть обработаны одновременно, поэтому следующие 30 запросов второго процесса не могут быть обработаны до тех пор, пока первые полны. В вашем Например, если вы установите http.globalAgent.maxSockets = 60;, то вызовы будут обрабатываться одновременно.

0 голосов
/ 17 августа 2011

Ну, на самом деле, я думаю, вы не порождаете запросы, которые можно отозвать обратно. У вас есть только один обработчик событий, и он выполняет цикл все подряд.

Можете ли вы найти, где Райан Даль дал этот разговор?

...