Кластеры

Цель

Освоить приемы многопоточной работы Node.js

Начальные требования

Знание Javascript. Понимание основ мультизадачности.

Введение

Одиночный экземпляр Node.js работает в отдельном треде. Для использования приимуществ многоядерных систем, пользователи могут хотеть запустить кластер Node.js процессов, чтобы разделить между ними нагрузку.

Модуль cluster позволяет легко создавать дочерние процессы, которые разделяют серверные порты.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

Работающий Node.js теперь разделяет порт 8000 между воркерами:

$ node server.js
Master 3596 is running
Worker 4324 started
Worker 4520 started
Worker 6056 started
Worker 5644 started

Как это работает

Процессы воркеров порождаются методом child_process.fork(), таким образом они могут общаться с родительским процессом по IPC и передавать обработчики серверов туда и обратно.

Модуль cluster поддерживает два метода распределения соединений.

Первый и установленный по умолчание: главный процесс слушает порт, принимает новые соединения и раздеяет их по воркерам по кругу, следя при этом, чтобы воркеры не перегружались.

Второй: главный процесс создает слушающий сокет и шлет его воркерам, воркеры в всвою очередь принимают соединения напряму.

Второй подход теоретически должен давать лучшую производительность,. Однако в действительности распределение при этом стремится стать очень несбалансированным изза причуд менеджера задач операционной системы. По наблюдениям 70% всех соединений попадают в всего два из восьми процессов.

Поскольку server.listen() отдает большинство работы главному процессу, существуют три случая когда поведение нормального процесса Node.js и воркера кластера отличается:

  1. server.listen({fd: 7}) B поскольку сообщение передано главному процессу, декриптор файла 7 будет прослушиваться в родительском процессе и управление будет передано воркер, вметсо прослушивания того, что воркер считает число 7 ссылкой на файл.
  2. server.listen(handle) Явное прослушивание источника заставит воркер использовать этот эсточник вместо общения с головным процессом.
  3. server.listen(0) Обычно это заставит серверы слушать случайный порт. Тем не менее, на кластере каждый воркер будет получать тот же самый "случайный" порт, каждый раз когда он выполняет listen(0). По сущетсву, порт является случайным первый раз, но является предсказуемым вдальнейшем. Для прослушивания уникального порта сгенерируйте номер порта основанный на ID воркера.

Node.js не обеспечивает логики роутинга, вот почему важно спроектировать приложение так, чтобы оно делало роутинг небольшими усилиями, на объектах в памети, для таких веще как логин и сессии.

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

Несмотря на то что основным использованием для модуля cluster являются сетевые операции, он может быть использован для других целей, для которых могут понадобиться воркеры.

Класс: Worker

Объект Worker содержит всю публичную информацию и методы воркера. В мастере достуть к нему может быть получен используя cluster.worker.

Полное описание класса Worker, его свойств и методов см. в официальной документации.

Практическое задание

  1. Создать простейший http-сервер на порт 8000.
  2. Сделать его многопоточным с использованием кластеров.
  3. Сделать так, чтобы любой запрос возвращал номер кластера, на котором он отработал.

results matching ""

    No results matching ""