Если вы, как и я, изучали Node.js, скорее всего, мы начали свое путешествие в одном и том же месте. Что касается Hello World, я считаю, что его довольно сложно превзойти. С помощью всего нескольких строк кода и нулевого времени компиляции/связывания у нас есть настоящий HTTP-сервер, который работает и обслуживает.

После того, как вы стали свидетелем такого колдовства, следующим логическим шагом было бы погрузиться и выяснить, что еще возможно. Именно здесь вы встретитесь с различными учебными пособиями и примерами, которые всегда будут (повторно) объяснять асинхронную, управляемую событиями, однопоточную архитектуру. Я полностью понимаю, что это делает очень эффективную, быструю платформу и (если написано хорошо) очень быстрые приложения. У меня был только один простой вопрос: «Есть ли у него смехотворный режим»?

Оказывается, это делает, ну, как бы, многое зависит от приложения, которое вы создаете (мы вернемся к этому позже). С помощью модуля cluster теперь мы можем запускать несколько процессов и использовать для них общие серверные порты. В качестве дополнительного бонуса Node и ОС волшебным образом управляют балансировкой нагрузки за нас. Говоря об ОС, Windows получает несколько почетных упоминаний на этой странице, но я отвлекся, вернемся к делу.

кластеризованная версия «hello world» такая же простая и мощная, как и ее старший брат. Попробуйте, но имейте в виду, что на первый взгляд/выполнение/нагрузочный тест он не даст значительного (если таковое имеется) улучшения производительности по сравнению с версией с одним процессом. Не бойтесь, потому что есть множество руководств/примеров, которые демонстрируют прирост производительности, который можно получить за счет «нагрузки» на систему/приложение.

Я здесь по простой причине, для отладки. Достаточно сложно отладить один процесс. Пошаговое выполнение асинхронного кода сопряжено со своими уникальными проблемами. Умножьте это на 8, и вас ждут часы неумолимого веселья. Я знаю, что хочу перейти в режим полного кластера, когда в конечном итоге развернусь в дикой природе, а пока — каковы мои варианты сдерживания монстра?

  1. Используйте параметр командной строки
  2. Создайте два «основных» файла и запустите соответствующий по мере необходимости.
  3. Используйте файл конфигурации, чтобы включить или отключить режим кластера

Я добавил в другие просто для удовольствия, правда в том, что я всегда выбираю вариант с файлом конфигурации. Во-первых, у меня очень плохая память, параметры командной строки выбрасывают меня каждый раз, даже те, которые я использую ежедневно. Почему я не рассматриваю вариант 2? См. причину для варианта 1. Если серьезно, я не рекомендую адаптировать дизайн вашего приложения для размещения внешней функции. Лучше спроектируйте приложение так, чтобы оно работало как единый процесс с единой точкой входа, без учета кластеризации. Пока кажется, что слов много, так что давайте сосредоточимся на некоторых кодах…

Структура каталогов примера приложения выглядит следующим образом:

│   index.js
├───config
│   ├──cluster.js
│   └──server.js
└───main
    └──server.js

/config/server.js

module.exports = {
  port : 3000,
  hostname : '127.0.0.1'
};

/main/server.js

const http = require('http');
const webserverConfig = require('../config/server');
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});
server.on('listening', () => {
  console.log(`Server running at  http://${webserverConfig.hostname}:${webserverConfig.port}/`);
});
server.on('error', (err) => {
  console.log(`Server error : ${err}`);
  process.exit(1);
});
server.listen(webserverConfig.port, webserverConfig.hostname);
process.on('SIGINT', async () => {
  console.log(`Server caught SIGINT`);
  try {
    await server.close();
    console.log(`Server closed`);
  } catch (ex) {
    console.log(`Server close error ${ex}`);
  }
  process.exit();
});

Здесь у нас есть HTTP-сервер, в основном «hello world» с небольшими изменениями. Имя хоста и номер порта извлекаются из файла конфигурации. Там также есть некоторая обработка ошибок, добавленная для хорошей меры. Мое личное мнение: как бы ни было интересно узнавать что-то новое, вам также необходимо узнать, что может пойти не так, и как реагировать, когда что-то идет не так. К кластеру…

/config/cluster.js

const os = require('os');
module.exports = {
  enabled : false,
  numProcesses : os.cpus().length
}

index.js

const cluster = require('cluster');
const clusterConfig = require('./config/cluster.js');
if(clusterConfig.enabled && cluster.isMaster) {
  console.log(`Process ${process.pid} starting (Master)`);
  
  // Fork workers.
  for (let i = 0; i < clusterConfig.numProcesses; i++){
    cluster.fork();
  }
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Process ${worker.process.pid} died`);
  });
  process.on('SIGINT', () => {
    console.log(`Process caught SIGINT (Master)`);
  });
} else {
  console.log(`Process ${process.pid} starting`);
  require('./main/server.js');
}

Файл index.js служит двум целям, теперь он становится «основным» файлом. Единая точка входа в наше приложение. Он также отвечает за управление кластером — если он включен. По большей части код аналогичен примеру кластера. Когда приложение запускается, оно определяет, включен ли кластер, и ведет себя следующим образом:

  • Если включено и я являюсь основным процессом, разветвить {numProcesses} рабочих процессов.
  • Если включено и я являюсь рабочим процессом, запустите новый «серверный» процесс.
  • Если отключено, запустите новый «серверный» процесс.

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

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

Это все на сегодня, ребята, надеюсь, что это была веселая поездка, и более того, она была полезной.

P.S. Чтобы увидеть обработку ошибок в действии, запустите приложение из терминала с помощью node ., а затем нажмите ‹Ctrl›+C. Вы также можете попробовать запустить два экземпляра приложения и посмотреть, что произойдет.