Кластеризация Nodejs с помощью Sticky-Session

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



  var sticky = require('sticky-session');
  var express = require('express');
  var app = express();

  app.get('/', function (req, res) {
      console.log('worker: ' + cluster.worker.id);
      res.send('Hello World!');
  });


  var server = http.createServer(app);
      sticky.listen(server,3000);

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

Я просмотрел документацию по кластеризации nodejs и sticky-session, а также еще один ответ о переполнении стека по этому поводу.

  var cluster = require('cluster');
  var http = require('http');
  var sticky = require('sticky-session');
  var express = require('express');
  var app = express();

  app.get('/', function (req, res) {
      console.log('worker: ' + cluster.worker.id);
      res.send('Hello World!');
  });


  var server = http.createServer(app);
      sticky.listen(server,3000);

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

Я читал, что есть альтернатива sticky-cluster. Может ли кто-нибудь дать правильный авторитетный ответ по этой теме, который будет полезен для людей, которые ищут то же самое, и еще одна основная проблема связана с этим - это объект app.locals, который используется для хранения переменных для экземпляр приложения и появление нескольких экземпляров сервера приводят к тому, что это ломается, поскольку значения будут разными в разных экземплярах, поэтому этот подход вызывает большую проблему, и приложение ломается, поэтому. приблизиться к его преимуществам и недостаткам.

Я не ищу ответ, который ограничивается использованием модуля sticky-sessions nodejs, я приветствую все другие подходы, в которых используются все ядра процессора и но обеспечивается непрерывность сеанса.

Если это связано с RedisStore или MongoDb, все в порядке. Я хочу знать о стандартном подходе в случае приложения nodejs с кластеризацией с непрерывностью сеанса.

https://github.com/indutny/sticky-session

https://nodejs.org/api/cluster.html

https://stackoverflow.com/a/37769107/3127499


person codefreaK    schedule 12.07.2018    source источник
comment
я действительно не знаю ответа, но попробуйте переместить var sticky = require('sticky-session'); в начало кода, чтобы все они использовали один и тот же сеанс.   -  person Amit Wagner    schedule 18.07.2018
comment
Нет не помогает   -  person codefreaK    schedule 18.07.2018


Ответы (3)


В вашем коде есть небольшая проблема. Модуль "sticky-session" уже использует модуль node.js "cluster". Вам не нужно "fork()", потому что sticky- session уже сделает это за вас. Давайте узнаем, как:

var cluster = require('cluster'); // Only required if you want the worker id
var sticky = require('sticky-session');

var server = require('http').createServer(function(req, res) {
  res.end('worker: ' + cluster.worker.id);
});
sticky.listen(server, 3000);

вызов sticky.listen() уже создаст для вас рабочих. См. реализацию listen() ниже.

 function listen(server, port, options) {
  if (!options)
    options = {};

  if (cluster.isMaster) {
    var workerCount = options.workers || os.cpus().length;

    var master = new Master(workerCount, options.env);
    master.listen(port);
    master.once('listening', function() {
      server.emit('listening');
    });
    return false;
  }
  return true;
}

Эта строка var master = new Master(workerCount, options.env) отвечает за порождение воркеров. см. реализацию Master() ниже:

function Master(workerCount, env) {
  net.Server.call(this, {
    pauseOnConnect: true
  }, this.balance);

  this.env = env || {};

  this.seed = (Math.random() * 0xffffffff) | 0;
  this.workers = [];

  debug('master seed=%d', this.seed);

  this.once('listening', function() {
    debug('master listening on %j', this.address());

    for (var i = 0; i < workerCount; i++)
      // spawning workers
      this.spawnWorker();
  });
}

Так что действительно, когда вы вызываете sticky.listen(server,port), вы на самом деле вызываете cluster.fork(). Следовательно, вы не должны явно снова вызывать fork(). Теперь ваш код должен выглядеть так:

var cluster = require('cluster'); // Only required if you want the worker id
var sticky = require('sticky-session');

var server = require('http').createServer(function(req, res) {
  res.end('worker: ' + cluster.worker.id);
});

//sticky.listen() will return false if Master
if (!sticky.listen(server, 3000)) { 
  // Master code
  server.once('listening', function() {
    console.log('server started on 3000 port');
  });
} else {
  // Worker code
}

Важно помнить, что у порожденных воркеров будет свой собственный EVENTLOOP и память, поэтому ресурсы не распределяются между собой. Вы можете использовать «REDIS» или другие модули npm, такие как «memored», для совместного использования ресурсов между разными работниками.

Надеюсь, это решит ваши обе проблемы.

person Vasi Anurag    schedule 19.07.2018
comment
vasi Я ищу рабочую реализацию этого. Я просмотрел код после того, как выпустил свою примерную программу и узнал о том, что вы здесь упомянули. Его вопрос больше о непрерывности сеанса, то есть первом запросе, который обрабатывается порожденным работником как убедиться, что второй последующий запрос обрабатывается одним и тем же работником. Я думал, что sticky-session утверждал, что гарантирует, что. У меня нет навязчивой идеи придерживаться модуля sticky-session. Я хочу знать, как обеспечить непрерывность сеанса с помощью кластеризации . - person codefreaK; 19.07.2018
comment
chat.stackoverflow.com/rooms/176317/nodejs-clustering приходите в чат, и вы узнаете это - person codefreaK; 19.07.2018

Я думаю, вы путаете закрепленный сеанс с хранилищем в общей памяти.

Попробую помочь:

  • Модуль Sticky-sessions балансирует запросы, используя их IP-адреса. Таким образом, клиент всегда будет подключаться к одному и тому же рабочему серверу, а socket.io будет работать как положено, но на нескольких процессах!

Внедрение закрепленных сеансов означает, что теперь у вас есть несколько узлов, принимающих соединения. Однако это НЕ гарантирует, что эти узлы будут РАЗДЕЛЯТЬ одну и ту же память, поскольку у каждого рабочего процесса есть собственный цикл обработки событий и состояние внутренней памяти.

Другими словами, данные, обрабатываемые одним узлом, могут быть недоступны другим рабочим узлам, что объясняет указанную вами проблему.

... еще одна основная проблема заключается в том, что это объект app.locals, который используется для хранения переменных для экземпляра приложения, и появление нескольких экземпляров сервера приводит к его поломке, поскольку значения будут разными в разных экземплярах, поэтому этот подход вызывает большую проблему и приложение ломается...

Таким образом, чтобы решить эту проблему, нам потребуется использовать что-то вроде Redis, чтобы данные могли совместно использоваться несколькими узлами.

Надеюсь это поможет!

person alchuang    schedule 22.09.2019

Если я правильно понял ваши вопросы, вы имеете дело с хранилищем данных в памяти или хранилищем сеансов. Это одна из известных проблем аутентификации на основе сеанса в нескольких узлах или в кластере. Предположим, вы сделали вызов к узлу A и получили сеанс с именем sessionA, но для следующего вызова вы сделали его к узлу B. Узел B ничего не знает о сеансе A. Люди пытаются решить эту проблему, используя липкую сессию, но этого недостаточно. Хорошей практикой будет использование альтернативного подхода, такого как JWT или oAuth2. Я предпочитаю JWT для сервисного обслуживания. JWT ничего не хранит и не имеет состояния. Он прекрасно работает с REST, поскольку REST также не имеет состояния. Здесь https://tools.ietf.org/html/rfc7519 — спецификация JWT. реализация. Если вам нужен какой-то токен обновления, в этом случае вам нужно подумать о хранилище. Хранилищем может быть что угодно, например REDIS, MongoDB или любая другая БД на основе SQL. Для дальнейшего разъяснения о JWT в nodejs:

https://jwt.io/

https://jwt.io/introduction/

https://www.npmjs.com/package/jsonwebtoken

https://cloud.google.com/iot/docs/how-tos/credentials/jwts#iot-core-jwt-refresh-nodejs

person Omar Faroque Anik    schedule 24.07.2018