Подтвердить что ты не робот

Производительность UDP в Node.js

Я сравниваю Java UDP-клиент, который непрерывно отправляет дейтаграммы с полезной нагрузкой в ​​100 байт как можно быстрее. Он был реализован с помощью java.nio.*. Тесты показывают, что он способен обеспечить устойчивую пропускную способность 220 тыс. Дейтаграмм в секунду. Я не тестирую сервер; клиент просто отправляет дейтаграммы на какой-то неиспользуемый порт на localhost.

Я решил запустить тот же тест в Node.js, чтобы сравнить обе технологии, и было удивительно грустно видеть, что Node.js выполняется в 10 раз медленнее, чем Java. Позвольте мне пропустить вас через мой код.

Сначала я создаю UDP-сокет, используя Node.js модуль dgram:

var client = require('dgram').createSocket("udp4");

Затем я создаю функцию, которая отправляет дейтаграмму с помощью этого сокета:

function sendOne() {
    client.send(message, 0, message.length, SERVER_PORT, SERVER_ADDRESS, onSend);
}

Переменная message - это буфер, созданный из строки со ста символами при запуске приложения:

var message = new Buffer(/* string with 100 chars */);

Функция onSend просто увеличивает значение переменной, которая содержит количество дейтаграмм, отправленных до сих пор. Затем у меня есть функция, которая постоянно вызывает sendOne() с помощью setImmediate():

function sendForever() {
    sendOne();
    setImmediate(sendForever);
} 

Изначально я пытался использовать process.nextTick(sendForever), но я узнал, что он всегда ставит себя на вершину очереди событий, даже до событий IO, как docs говорит:

Он запускается до того, как все последующие события ввода-вывода (включая таймеры) будут гореть в последующих тиках цикла событий.

Это предотвращает событие отправки сообщений ввода-вывода, поскольку nextTick постоянно помещает sendForever в конец очереди при каждом тике. Очередь растет с непрочитанных событий ввода-вывода, пока не произойдет Node.js crash:

fish: Job 1, 'node client' terminated by signal SIGSEGV (Address boundary error)

С другой стороны, setImmediate срабатывает после обратных вызовов событий ввода-вывода, поэтому я использую его.

Я также создаю таймер, который каждые 1 секунду выводит на консоль сколько дейтаграмм было отправлено за последнюю секунду:

setInterval(printStats, 1000);

И, наконец, я начинаю отправлять:

sendForever();

Запуск на том же компьютере, на котором выполнялись тесты Java, Node.js обеспечил устойчивую пропускную способность 21 тыс. дейтаграмм в секунду, в десять раз медленнее, чем Java.

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

function sendForever() {
    send();
    send();  // second send
    setImmediate(sendForever);
}

Но это не изменило пропускную способность.

У меня есть репозиторий, доступный на GitHub с полным кодом:

https://github.com/luciopaiva/udp-perf-js

Просто скопируйте его на свой компьютер, cd в папку и запустите:

node client

Я хочу открыть дискуссию о том, как этот тест может быть улучшен в Node.js, и если есть какой-то способ, мы можем увеличить пропускную способность Node.js. Любые идеи?

P.S.: для заинтересованных, вот часть Java.

4b9b3361

Ответ 1

Этот тест ошибочен. UDP не гарантирует доставку чего-либо и не гарантирует, что он выдаст какую-либо ошибку в случае ошибки.

Ваше приложение могло отправлять 1000 тыс. дейтаграмм/с на 1 ГБ/с из приложения Java, но 90% датаграмм никогда не доходили до адресата... возможно, что назначение не может быть запущено.

Если вы хотите провести любое тестирование UDP, вам понадобятся два приложения: по одному на каждом конце. Отправьте нумерованные дейтаграммы 1, 2, 3... и проверьте, что отправлено и что получили. Обратите внимание, что UDP не гарантирует упорядочения сообщений.

Ядра управляют локальной сетью по особым причинам. Есть огромные буферы, предназначенные для него, и более высокие пределы, ни один трафик никогда не проходит через сетевые карты или драйверы. Это очень отличается от отправки пакетов для реального.

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

PC1 <-----> switch <-----> PC2

Скажем, в одной комнате есть два компьютера, соединенных переключателем. Было бы небезопасно достигать 10k/s UDP-дейтаграмм на этой простой настройке, не теряя сообщений в случайном порядке.

И это только два компьютера в одной комнате. Это может быть намного хуже в Интернете и на большие расстояния.