Я сравниваю 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.