Pull to refresh

Comments 12

Есть еще несколько способов, но все они либо не кроссплатформенны либо также зависят от I/O.

Shared memory вполне себе кросс-платформенный и дает нулевые издержки по ресурсам. Давно планирую статью на хабр о переходе с Redis на Shared memory с STL-контейнерами, с графиками и впечатлениями…
что-то оно не работает =( с локальным сервером пишет 30 секунд на реквест, удаленным — 60
Довольно поздно прочитал Вашу статью.
Хотелось бы уточнить, пробовали ли что-нибудь наподобие https://github.com/plaidev/minimum-rpc?
Нашел модуль с очень компактный код. Там поднимается WebSocket-сервер на основе socket.io, ну, и, соответственно, используется WebSocket-клиент.
Хотелось бы, попросить Вас провести бенчмарк того же minimum-rpc с условиями бенчмарков, которые вы описывали в статье.
Вы имеете в виду сравнить скорость работы HTTP и WebSockets? Сейчас можно тестировать только HTTP эндпоинты, но сделать TCP WebSocket коннект несложно, просто непонятно зачем.
Дело в том, что сам по себе socket.io обещал давать неплохие результаты при использовании тысяч различных постоянных соединений.
В основном, речь идет о клиентах как браузеров. Но, как я понимаю, ваша проблема проявлялась в виде блокировок event-loop в процессе сервера.
Ну, и не даром в minimum-rpc используют socket.io вместо, например, стандартных http или process.send().

Интересно было бы посмотреть на сравнительные результаты.

Возможно, также проблема решения со 100 процессами и process.send() могла заключаться в том, что каждый процесс выполнял слишком мало задач и сразу завершался. Из статьи неизвестно, как организован пулл процессов. Вы запускали 100 процессов и потом раздавали им задачи, или Вы организовывали пулл, который запускает 100 одновременных процессов, и каждый процесс выполнял одну или несколько задач и завершался, а на месте завершенного процесса пуллом запускался новый?

По моему скромному мнению, второй способ предпочтительнее, более защищен от утечек, защищен от различных необработанных исключений. Но тут надо понимать, что каждый новый процесс кушает при запуске довольно много CPU-time, и нужно соблюдать пропорцию, хотя бы:
Runtime_CPU_time * Proc_count < Execute_CPU_time, где:
  • Runtime_CPU_time — это количество затраченного CPU-time для запуска процесса;
  • Execute_CPU_time — это сумма затраченного CPU-time для выполнения всех задач порученных одному процессу;
  • Proc_count — количество одновременно запущенных процессов.

Если проблема 100 процессов заключалась в I/O, то тут необходимы испытания на разных версиях ядер ОС. Пробовали ли Вы запускать тесты на FreeBSD? Сомневаюсь, что проблема производительности process.send() упирается в I/O. Всегда есть вариант разгрузить количество (частоту) использования process.send() и объем информации, передаваемой через process.send(), в пользу самостоятельной загрузки данных процессом и последующим самостоятельным сохранением результатов.

Сколько раз сам использовал process.send(), с Вашими трудностями я не сталкивался. Правда, в последний раз это было давно, так я использовал для запуска тех скриптов NodeJS версий 0.6.x (если мне не изменяет память).

Как-то, в свое время, мне необходимо было написать парсер VK для расчетов различных статистических данных. На самом деле, действие происходило чуть позднее новости о том, что Яндекс стал индексировать VK. Необходимо было использовать максимум доступных ресурсов для обработки максимального объема информации за ограниченное время. На тот момент, располагали тремя арендованными в ЦОД серверами с различными характеристиками.

Я использовал комбинированный подход, то есть, master-процесс запускал несколько worker-процессов, каждый из которых выполнял задачи в несколько потоков. Причем, для использования максимума свободных ресурсов, master-процесс в процессе выполнения руководил количеством одновременно запущенных процессов и количеством потоков в каждом worker-процессе. Master-процесс различал несколько состояний ОС: если весь chain процессов превышал общий Memory_limit, master-процесс уменьшал количество процессов и увеличивал количество потоков; если worker-процессы превышали CPU_limit, он увеличивал количество процессов и уменьшал количество потоков в каждом процессе. Также master-процесс останавливал перегруженные и idle процессы.

Для реализации использовал простые инструменты:
  • для запуска worker-процессов использовал child_process.spawn() (позднее появился Cluster);
  • для передачи данных между процессами использовал process.send();
  • для выполнения задач в несколько потоков использовал async.queue() модуля async.

Возможно, последние версии NodeJS перегружены функционалом. В те времена V8 умел использовать только 32-битную архитектуру ОС. Опять же, если мне не изменяет память, свеже-запущенный процесс node занимал 1.8Мб оперативной памяти, а процесс с запущенным парсером html-страниц занимал памяти около 15Мб и точно не более 32Мб (был индивидуальный heap-лимит, превышая который, процесс считался зависшим и перезапускался).
Спасибо за подробный комментарий. У вас фундаментальное заблуждение. У ноды только один рабочий поток (async.queue использует обычные js колбеки, которые не что иное как очередь функций и event loop) поэтому как бы не был организован сервер, если он не спаунит процессы, то запросы будут идти через event loop.
Поэтому разработчики socket.io не могут ничего утверждать о производительности, они лишь сделали обертку для WebSockets на клиенте и TCP обертку на сервере.
По поводу process.send как раз и идет речь в статье, нода использует TCP для коммуникации, а значит I/O а значит падение производительности.
Но если держать процессы запущенными и коммуникация между ними не принципиальна, то производительность процессов и потоков должна быть по идее сравнимой, если у ОС есть достаточно ресурсов на все открытые процессы.
У ноды только один рабочий поток

Об этом я в курсе. В моем примере с VK, парсинг страницы, разумеется происходит гораздо быстрее, чем ее закачка. Во время закачки CPU простаивает. При этом процесс остается запущенным, данные для работы уже загружены в память. Было бы глупо скачивать VK в 1 поток/процесс. В разное время ситуация со скоростью доступа к VK меняется. Необходимо контролировать несколько параметров, вызывать события контроля чисел потоков/процессов при пробивании min-max-лимитов.

Я не утверждаю, что socket.io будет магически-быстрее или менее затратным по ресурсам. Можно же просто следить за пулами процессов, чтобы они не перегружали друг друга и при этом работали на максимуме своих возможностей?

Пишу комментарии для того, чтобы выяснить сколько % профита принесло Вам ваше решение.
Уже который комментарий пишу, и все забываю сказать. Через socket.io есть возможность передавать данные в формате BSON, который в отличии от синхронного JSON, имеет асинхронный по-блоковый алгоритм генерации и парсинга. Другими словами, умеет ReadableStream и WritableStream, прямо как SAX у XML.
В целом, вы правы, но мне все же интересно вычислить реальные цифры. Статье больше трех лет, по меркам nodejs — это целое поколение в эволюции скриптов node :-) Думаю, когда встанет в очередной раз этот вопрос, запилю бэнчмарки самостоятельно, может даже, напишу об этом статью на Хабр.
Но опять же, socket.io значит нода, нода значит один поток и много процессов, кол-во процессов ограничено ресурсами, поэтому в статье юзается одновременный запуск сразу с кучи EC2 инстансов
В моем случае профит составил десятки раз. На одной и той же машине решение с процессами достигало максимум 300 RPS, с потоками — 2000-5000. Я опять же говорю, что все зависит от задачи и необходимости коммуникации между инстансами, но так или иначе странно ожидать от потоков меньшей производительности.
Sign up to leave a comment.

Articles