Процессы решают другую задачу, чем workers: изолируют приложение и масштабируют его на несколько ядер без общей памяти. За изоляцию платят IPC и внешним состоянием.
child_process для внешних программ
child_process запускает другую программу: мигратор, конвертер, системную утилиту. Для известного бинарника и аргументов выбирайте execFile, а не shell-строку.
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
const { stdout } = await execFileAsync('git', ['rev-parse', '--short', 'HEAD']);
console.log(stdout.trim());
Так аргументы не проходят через shell, и содержимое параметра не становится командой. Таймаут, лимит вывода и код завершения проверяют обязательно.
cluster для копий HTTP-приложения
cluster создает несколько Node-процессов. Операционная система распределяет между ними входящие соединения на одном порту. У каждого процесса своя куча и свой event loop.
Поэтому Map в памяти - не общий кеш и не общий счетчик идемпотентности. Данные, которые должны совпадать между workers, выносят в Redis, PostgreSQL или другое внешнее хранилище.
Число workers берут по профилю нагрузки и памяти, а не автоматически по всем ядрам. Каждая копия потребляет собственный RSS и держит собственные пулы соединений.
Завершение и рестарты
При выкате старый worker перестает принимать новые соединения, закрывает сервер прослушивания, дожидается текущих запросов и только потом завершается. Таймаут принудительного выхода нужен на случай зависшего запроса.
Простая репликация состояния process.send между workers не заменяет транзакцию и не делает deploy атомарным. Для health checks оркестратора важен момент, когда процесс перестал быть ready, а не момент вызова disconnect.
Коротко
execFileзапускает программу с массивом аргументов без shell.- Cluster масштабирует HTTP-приложение на несколько изолированных процессов.
- Общий порт не означает общую память.
- Идемпотентность и кеши между workers живут во внешнем хранилище.
- Остановка состоит из запрета новых соединений, ожидания текущих и таймаута.
Что почитать дальше
- Worker threads - когда вычисление лучше вынести внутрь процесса.
- Таймауты и отмена - как не оставить зависший запрос навсегда.