В чем заключается принцип работы кластеров Node.js и как они помогают масштабированию приложения
Что проверяют
Понимаете ли вы, что кластер это несколько процессов, а не потоков, и какие последствия у этого для состояния приложения. Следом спросят, чем cluster отличается от worker_threads и зачем он при нескольких репликах в Kubernetes.
Как отвечать
JavaScript в Node.js выполняется в одном потоке на процесс (у libuv есть пул потоков, по умолчанию четыре, но он для файлов, DNS и криптографии), поэтому один процесс нагружает одно ядро. Модуль node:cluster запускает несколько копий приложения:
- primary: главный процесс, проверка
cluster.isPrimary(староеisMasterустарело). Он вызываетcluster.fork(), следит за воркерами и держит с ними IPC-канал; - воркеры: отдельные процессы Node.js со своим V8, памятью и event loop. Все вызывают
listenна одном порту, а сокет фактически открывает primary; - распределение: по умолчанию везде, кроме Windows, primary принимает соединения и раздаёт их по кругу (round-robin). Политику меняет
cluster.schedulingPolicyилиNODE_CLUSTER_SCHED_POLICY; - живучесть: на событие
exitprimary поднимает новый воркер, упавший процесс не роняет весь сервис.
Число воркеров берут из os.availableParallelism(), а не из os.cpus().length. В контейнере с лимитом CPU число ядер хоста может не совпадать с выделенной квотой, там его лучше задавать явно.
Главное следствие: общей памяти между воркерами нет. Сессии, кэш, счётчики rate limit в памяти процесса у каждого свои, их выносят в Redis. WebSocket через Socket.IO требует sticky sessions и адаптер для обмена событиями между процессами.
На практике cluster вручную пишут редко: берут PM2 в cluster mode или несколько реплик в Kubernetes по одному процессу на контейнер. Для тяжёлых вычислений внутри одного процесса подходит worker_threads.
Где спотыкаются
Говорят, что кластер добавляет многопоточность. Это процессы, и данные между ними передаются только через IPC или внешнее хранилище.
Хранят сессии в памяти и удивляются, что пользователя разлогинивает при попадании на другой воркер.
Думают, что кластер ускорит тяжёлый синхронный код. Один такой запрос по-прежнему блокирует свой воркер, а воркеров не больше, чем ядер.