Как стать автором
Обновить

Приручаем многопоточность в Node.js (часть 5/5: автомасштабирование под нагрузку)

Время на прочтение19 мин
Количество просмотров7K
Всего голосов 15: ↑15 и ↓0+15
Комментарии6

Комментарии 6

К сожалению стоит признать что многопоточность и нода это практически несовместимые вещи. Параллелить на уровне независимых процессов завернув приложение в докер - еще куда не шло. Но появляется оверхед по ресурсам, коннектам к БД

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

Вобщем решил я это дело замногопоточить в рамках одного процесса, помучался NodeJS worker_threads и понял что надо бы это дело на Go попробовать. И вуаля, аналогичный код на Go в проде теперь жрет 200-400 мб и 2-4 ядра и все это один процесс, при этом выполняет очень много операций. По итогу получилось что нагрузка упала примерно в 50 раз, но объем работы выполняется тот же.

Ноду я конечно из-за этого случая не разлюбил, но выводы сделал что для многопоточки хорош Go, возможно и Rust тоже не плох для этого, но до него я так и не добрался.

Все-таки чтобы полностью ощутить преимущество другого языка, надо переписать существующее приложение целиком, а это не всегда возможно и оправданно. Но вот так повысить производительность JS за счет многопоточности иногда можно.

Строго говоря это и не многопоточность, это банальное масштабирование. Многопоточность как термин воркеров, здесь мало уместен.

Да иногда можно выиграть. Но с другой стороны, если нужен настоящий выигрыш - легче написать модуль нативный который по настоящему многопоточно что то гоняет а не извращаться подобным образом. Впрочем, зависит от задач.

Если worker_threads - это не про потоки, то про что же?

Ну, и написать нативный модуль - это почти никогда не "легче".

Про что? Про IPC обозванным потоками! :)
На уровне абстракции вы правы - воркеры можно рассматривать как потоки. Однако на деле, под капотом работает почти отдельный самостоятельный инстанс движка, который разделяет родителя\потомка, перенаправляет банально stdin\out итд итп. По итогу мы имеем оверхед просто от существования "потока" в виде инстанса, потребляемой памяти и прочей радости в виде больших потерь на том самом IPC, чего на нативных слоях даже в помине нету кроме условных одного - другого десятка байт на хэндл и прочей метаинфы.

Не поймите меня не правильно. Я сам люблю ноду. Однако как уже выше верно сказано - нода не про многопоточность. Нода в принципе не про многопоточность. И никогда не будет. Как и не будет про тяжелую нагрузку.

А в чем сложность написания нативного модуля? Сейчас (да и думаю уже давно) есть решения которые позволяют это делать одной двумя кнопками. Либо если не нативный модуль то можно делегировать тяжелую задачу на какой нибудь бинарь.

Вот что наличие отдельного инстанса V8 внутри дает издержки - согласен, но они точно такие же, как и в основном потоке. И мы ведь осознанно идем на них, выбирая транслируемый ЯП вместо компилируемого.

А вот "не про тяжелую нагрузку" - так это надо внимательно на прикладную задачу смотреть. Грубо, если я использую те же регулярки, http или любой другой нативный модуль, или если сам V8 сгенерил по моему JS-коду байткод с использованием SSE - так ли уж велики издержки относительно вызова тех же функций в C++/Rust/...?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий