2014-02-01 2 views
4

У меня проблема с производительностью с помощью http-модуля Node.js.Node.js HTTP-ответная локальная производительность

Я пишу небольшой скрипт узла, чтобы сделать веб-сервер, который отправляет данные на указанную продолжительность (в секундах). Цель состоит в том, чтобы проводить тесты скорости между клиентами и сервером. И местные тесты скорости тоже. Для этого я использую пользовательский Readable, который я 'pipe' для ответа http. Этот пользовательский Readable прекращает отправку данных по истечении заданной продолжительности. Данные представляют собой циклический буфер произвольных значений.

Код выглядит так:

var http = require("http"); 
var Readable = require('stream').Readable; 
var url = require('url'); 

var buff = new Buffer(16384); // change this to bigger value for better local performances 
buff.fill(0); // data is filled with 0s here, change to whatever 

// this function creates a Readable that will provide data for <duration> seconds and then will nd. 
function createTimedReadable(duration) 
{ 
    var EndAt = Date.now() + 1000*duration; // when to end 
    var rs = new Readable(); 
    rs.EndAt = EndAt; 
    rs._read = function() { 
    if (Date.now() < rs.EndAt) 
     rs.push(buff); 
    else 
     rs.push(null); 
    } 
    return(rs); 
} 

// each client request will call this function 
function onHTTPrequest(request, response) { 
    var who = request.connection.remoteAddress + ":" + request.connection.remotePort; 
    console.log("Request received from " + who); 
    var duration = 10; // (actually we get it from the url query part) 
    // send the header: it's unknown size of binary data 
    response.writeHead(200, 
    { 
    'Content-Type' : 'octet-stream', 
    'Cache-Control' : 'no-cache, no-transform' 
    }); 

// link (pipe) the Readable to the response 
    var timedReadable = createTimedReadable(duration); 
    timedReadable.pipe(response); 
} 

// main - start the server 
http.createServer(onHTTPrequest).listen(8888); 
console.log("Server has started"); 

Для вызова сервера в течение 10 секунд:

wget -O /dev/null http://serverip:8888 

Данные отбрасывается на стороне клиента (/ DEV/нуль), потому что мы не делаем хотите, чтобы запись на диске замедляла работу и возилась с результатами теста скорости.

При проведении тестов «по проводам» (2 машины) скорость кажется прекрасной, но при выполнении локального теста (одна и та же машина на том же компьютере) скорость неправильная, очень медленная и очень зависящая от размера переменной Buffer (положительный эффект). Использование очень большого размера для этого буфера дает лучшую производительность. Например, на одной и той же тестовой машине:

with a 16k buffer, we get 188 MB/s 
with a 128k buffer, we get 487 MB/s 
with a 1M buffer, we get 558 MB/s 
with a reference code written in C using a 4K buffer, we get 626 MB/s 

«по проводам» мы не видим эту проблему, потому что мы используем GigaEthernet (Ge), поэтому мы не можем идти быстрее, чем ~ 110MB/с, но с более быстрыми проводами (например, 10Ge) мы были бы ограничены 188 МБ/с с 16-килобайтным буфером. Если мы используем 4K-буфер, такой как код C, чем код узла, он даже не достигает скорости Ge, мы получаем только 69 МБ/с.

Так что есть ограничение где-то, я не знаю, где. И я не понимаю, почему размер буфера влияет на производительность, так как мы используем цикл Readable. Bascially, почему вызов «push» 8 раз с 16k данными каждый раз медленнее, чем вызов его 1 раз с данными 128k (16k * 8).

ИЛИ Может ли быть другой способ сделать эффективные «ограниченные по времени потоки»?

Спасибо.

ответ

0

Накладные расходы. Было бы, безусловно, больше накладных расходов на передачу 8 блоков размером 128 КБ, чем 1 блок 1 МБ. Размер блока составляет только полезную нагрузку, но каждый запрос также имеет заголовки вместе с телом. Если вы учитываете накладные расходы, связанные с каждым запросом, ваши результаты будут похожи друг на друга.

Во-вторых, между узлом и сравнением C. Вероятно, что C превзойдет узел. Но то, что составляет тестовый код, также важно. Вы писали http-сервер для тестирования на C? В противном случае просто отправка блоков данных для теста скорости с помощью C будет просто измерять пропускную способность диска без накладных расходов.

0

Давайте упрощать, что узел делает на 3 вещи:

  1. Прочитать из читаемого потока
  2. Запись в гнездо

Поскольку ничего другого на сервере не выполняется, сервер делая то же самое снова и снова.

Если вы установили размер 1KiloBytes, то для каждого 1KiloBytes данных, как только мы прочитаем из сокета и как только мы напишем в сокет.

Если вы установили размер 128Bytes, то для каждого 1KiloBytes данных, 8 раз мы читаем из сокета и 8 раз пишем в сокет.

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

Это похоже на то, что вы приносите сумочки с парковки в свой дом. Что быстрее? Принесите все в один раз или принесите один за раз?

И всегда C быстрее, чем узел. узел содержит много абстракций, которые C не имеют каждого из своих собственных служебных данных. Существует неоптимизированный javascript, и есть этот материал для чтения/записи, который в C у нас просто есть writing to socket with a file descriptor.

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

Смежные вопросы