Node.js Кластерная архитектура: как масштабировать мастер работника

Я построил встроенную кластерную архитектуру Node.js. Приложение использует express для обслуживания API и статических файлов, и он развернут с помощью Docker:

[D O C K E R: 8080] --- N ---> [W O R K E R: 3001 ]  --- 1 ---> [M A S T E R: 3000]

У меня есть N работника в Worker.js и 1 мастер в master.js, Мастер и работник совместно используют общие модули, в то время как мастер имеет основной модуль, который загружает основные службы и предоставляет API для PORT=3001, работник загружает другой API на PORT=3000где контейнер Docker был привязан. В то время как прокси-сервер маршрутизации на Worker будет пересылать запросы ведущему устройству, чтобы обслуживать запросы к основным модулям, другие запросы напрямую передаются на сервер 3000.

Стартовый скрипт выглядит так

'use strict';
(function() {

/// node clustering
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) { // master node
    var masterConfig=require('./config/masterconfig.json');

    // Fork workers.
    var maxCPUs = process.env.WORKER_NUM || masterConfig.cluster.worker.num;
    maxCPUs=(maxCPUs>numCPUs)?numCPUs:maxCPUs;

    for (let i = 0; i < maxCPUs; i++) {
        const worker=cluster.fork();
    }

    var MasterNode=require('./lib/master');
    var master= new MasterNode(masterConfig);
    master.start()
    .then(done=> {
        console.log(`Master ${process.pid} running on ${masterConfig.pubsub.node}`);
    })
    .catch(error=> { // cannot recover from master error
        console.error(`Master ${process.pid} error`,error.stack);
        process.exit(1);
    });
}
else if (cluster.isWorker) { // worker node
    var workerConfig=require('./config/workerconfig.json');
    var WorkerNode=require('./lib/worker');
    var worker= new WorkerNode(workerConfig);
    worker.start()
    .then(done=> {
        console.log(`Worker ${process.pid} running on ${workerConfig.pubsub.node}`);
    })
    .catch(error=> { // worker error is recoverable
        console.error(`Worker ${process.pid} error`,error.stack);
    });
}

}).call(this);

У меня следующий вопрос.

1) По умолчанию cluster Модуль обмена разделяющим HTTP-соединением использует циклический подход для обслуживания запросов - см. здесь, где рабочие процессы создаются с помощью child_process.fork (). Я не знаю, смогу ли я настроить этот метод для распределения входящих соединений.

2) До сих пор я предоставляю статические файлы, шаблоны (например, pig / swig) в экспресс-веб-приложении для каждого работника на PORT=3000Это означает, что я запускаю статические маршруты для веб-приложения на каждом рабочем экземпляре. Я не уверен, что это, с точки зрения памяти, является лучшим подходом.

3) Другой кластерный подход. Я спрашивал о переносе этой архитектуры на PM2, несмотря на то, что она кажется многообещающей, я не уверен, что это лучший вариант - смотрите здесь для получения дополнительной информации.

1 ответ

Решение

Мастер должен заботиться только о том, чтобы запускать рабочих и правильно их выключать / следить за сигналами от хоста и реагировать соответствующим образом. По моему опыту, у меня были хитрые ошибки, потому что я показал API на мастере, который должен был быть на работнике.

Если вы планируете переключиться на PM2, PM2 будет обрабатывать ваш мастер, и вам все равно придется перенести этот код на работника (или, по крайней мере, так было раньше)

Относительно ваших вопросов;

  1. Если у вас есть необходимость переопределить циклический перебор или настроить его, я думаю, что у вас есть цель направить один и тот же клиентский трафик одному и тому же работнику, так называемым Sticky Sessions. Есть способы сделать это, но есть ограничения; если вы используете обратный прокси-сервер, такой как nginx или haproxy, перед узлом (что вам следует), а также хотите, чтобы сокеты работали должным образом (и в игре был Docker), вы не можете разветвляться на рабочих, потому что IP, который вы видите (на котором вы будете вычислять липкий идентификатор сеанса) всегда будет принадлежать вашему прокси или хосту докера (даже с заголовком x-forwarded-for), что в первую очередь побеждает цель кластеризации. -> Мое решение состояло в том, чтобы запустить каждого работника на новом порту (например, 3001, 3002 ... 300N) и позволить nginx обрабатывать обработку липких сессий
  2. Это не проблема, но не идеально - и да, память немного увеличится, потому что каждый работник загружает маршруты и модули. Но nginx намного быстрее обрабатывает статические файлы (и обрабатывает кеш для него с помощью многих http-заголовков), чем узел. Поэтому вы должны полагаться на nginx, обслуживающий статику, и сохранять узел для динамических запросов (например, /api /login и т. Д.)
  3. PM2 - это хорошее решение, которое имеет много продвинутых функций, таких как статистика отчетов и обеспечивает развертывание без простоев, а также стоит денег в зависимости от того, какие функции вы хотите использовать
Другие вопросы по тегам