[Из песочницы] Производительность межпроцессного обмена сообщениями в node.js

Разрабатывая приложение на node.js столкнулся с необходимости обмена сообщениями между процессами одной машины. В такой ситуации обычно я применял redis Pub/Sub, бонусом получая возможность масштабирования на несколько серверов. Но сейчас встал вопрос именно о локальном обмене и его производительности.

Я решил исследовать существующие варианты обмена сообщениями. Задача эта достаточно стандартна и известна как IPC (Inter Process Communications). Но что можно сделать на js и на какую производительность при этом рассчитывать?

Проведя серию тестов и получив результаты решил поделиться ими с хабрасообществом. Заинтересованных прошу под кат.

Варианты


Итак, 1й вариант, он же точка отсчета — redis Pub/Sub. В процессе тестирования redis-а выявилась достаточно приличная разница между tcp и unix socket режимами. Выбран был более производительный unix socket.

Далее стандартный api ChildProcess.send и process.on ('message'). Работает этот механизм используя каналы (pipе) между мастер процессом и запускаемым node процессом. Таким образом следующий вариант — pipe.

Следующие 2 варианта — сокеты: tcp и unix.

Методика и условия тестирования


Изначально мне нужно было отправить сообщение в центральный процесс и получить на него ответ. Таким образом, производительность далее будет измеряться именно в таких парах in-out сообщений. Каждый тестовый скрипт создает 1 мастер процесс, и W дочерних (workers). Во всех тестах совокупное количество процессов меньше доступных ядер на тестовой машине.

Каждый worker запускает C асинхронных циклов последовательной отправки и ожидания ответа фиксированного количества сообщений — симуляция конкурентных запросов в рамках 1 процесса.
В каждое сообщение помимо поле необходимых для теста, добавляется дополнительная строка paylod размером P.

Нативный ChildProcess.send использует JSON для сериализации сообщений. Поэтому тесты unix и tcp сокетов были так же построены с использованием JSON. Также был добавлен тест unix сокетов с использованием сериализации с помощью склеивания и ручной разборки строк.

Использовался node v4.4.5 и redis 3.0.

Результаты


По оси X описание теста вида WxC, payload=P. Например: 2×4 payload=100 — 2 worker-а, в каждом по 4 потока, размер дополнительных данных каждого сообщения — 100 байт.
payload=10

payload=100

payload=1000

Комментарии к результатам и выводы


Между pipe и unix сокетами разницы в производительности практически нет, tcp на 20–40% медленнее.

При использовании лишь одного конкурентного потока redis идет вровень с tcp, но при повышении конкурентности производительность не растет. Возможно все упирается в 1 общий для всех процессов входящий канал. Так или иначе, при выполнении тестов redis потреблял до 50% cpu 1 ядра, поэтому даже при попытках оптимизации на значительный прирост производительности рассчитывать не приходится.

Чем больше 1 сообщение, тем более оправдан отказ от JSON, но для относительно небольших сообщений и нагрузок разница не значительна.

Кстати, попытка использования для сериализации Buffer показала производительность не большую, а местами и меньшую, чем использование просто строк. Судя по всему, Buffer будет оправдан при преимущественно числовых полях в сообщении.

Итого. Если не нужно выжимать максимум производительности, встроенного ChildProcess.send вполне достаточно.

Возможный минус — вся нагрузка ложиться на мастер процесс, доходя до 90% cpu 1 ядра. Сокеты: более сложная реализация, но возможно получить большую производительность и вынести нагрузку на отдельный процесс. Так же возможен доступ из других программ. При использовании tcp сокетов можно выйти за пределы 1 машины. Предположу, что примерно такую максимальную производительность можно выжать при использовании ZeroMQ и тому подобных решений.

Исходные коды тестов доступны тут.

Комментарии (0)

© Habrahabr.ru