Продуктивність обміну повідомленнями між процесами в 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'). Працює цей механізм використовуючи канали (ріре) між майстер процесом і запускаються 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. Наприклад: 2x4 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 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.