Nodejs Clustering with Sticky-Session
Asked Answered
S

3

8
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`);
}

I looked up the documentation for nodejs clustering and sticky-session and another stack overflow answer regarding this

  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);

If the above snippet is run without forking it works fine but else never works as shown in the clustered example above in which the threads are started but server is never initialised .

I read there is alternative of sticky-cluster can somebody give a proper authoritative answer on this topic which will be useful for people looking for the same and the another main issue comes with this is the app.locals object which is used to store variables for an app instance and the occurrence multiple server instances causes this to break as values will be different across different instances so this approach causes a big issue and app breaks so .When answering please don't copy paste some code please give a detailed answer detailing the approach its benefit and short comings.

I am not looking for a answer that is limited to using sticky-sessions nodejs module, I welcome all other approaches in which all cores of the processor are used and but ensuring session continuity .

If it involves RedisStore or MongoDb store its ok,What I want to know is about a standard approach in case of nodejs application with clustering with session continuity

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

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

https://mcmap.net/q/1035270/-how-to-use-sticky-session-with-cluster-in-express-node-js

Stilwell answered 12/7, 2018 at 8:48 Comment(2)
i dont realy know the answer but try moving the var sticky = require('sticky-session'); to the begining of the code so they all use the same session.Upolu
No it does not helpStilwell
E
4

There is a small problem in your code. "sticky-session" module already uses node.js "cluster" module within.You dont need to "fork()" because sticky-session will already do it for you. Lets find out how:

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);

calling sticky.listen() will already spawn workers for you.See the listen() implementation below

 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;
}

This line var master = new Master(workerCount, options.env) is responsible for spawning workers. see the Master() implementation below:

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();
  });
}

So indeed when you call sticky.listen(server,port) you are actually calling cluster.fork().hence you should not explicitly again call fork(). Now your code should look like:

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
}

One important thing to remember is that spawned workers will have its own EVENTLOOP and memory hence resources are not shared among each other. You can use "REDIS" or other npm modules such as "memored" to share resources among different workers.

Hope this solves your both issues.

Esemplastic answered 19/7, 2018 at 9:7 Comment(2)
vasi I am looking for a working implementation of this I had gone through the code after I put out my sample program and found out about what you have mentioned here .his question is more about session continuity that is first request that processed by a spawned worker how to ensure the second follow up request is processed by the same worker I thought sticky-session claimed to ensure that .I don't have a obsession to stick with sticky-session module .I want to know how to ensure session continuity with clustering .Stilwell
chat.stackoverflow.com/rooms/176317/nodejs-clustering come to the chatroom will fill you up on thisStilwell
T
3

I think you are confusing sticky session with shared memory store.

Let me try to help:

  • Sticky-sessions module is balancing requests using their IP address. Thus client will always connect to same worker server, and socket.io will work as expected, but on multiple processes!

Implementing sticky sessions means that you now have multiple nodes accepting connections. However, it DOES NOT guarantee that these nodes will SHARE the same memory, as each worker has their own eventloop and internal memory state.

In other words, data being processed by one node may not be available to other worker nodes, which explains the issue you pointed out.

...another main issue comes with this is the app.locals object which is used to store variables for an app instance and the occurrence multiple server instances causes this to break as values will be different across different instances so this approach causes a big issue and app breaks...

Thus, to resolve this, we would require using something like Redis so that data can be shared across multiple nodes.

Hope this helps!

Thrum answered 22/9, 2019 at 6:6 Comment(0)
C
0

If I understand your questions correctly, you are dealing with in-memory data storage or session storage. This is one of the known problems in the session based authentication in multi-node or in a cluster. Suppose you made a call to Node A and get the session called sessionA but for the next call you made it to Node B. Node B does not know anything about sessionA. People try to solve this issue by using sticky session but that is not enough. Good practice will be to use an alternative approach, such as JWT or oAuth2. I prefer JWT for service to service communication. JWT does not store anything and stateless. It works brilliantly with REST since REST is also stateless. Here https://www.rfc-editor.org/rfc/rfc7519 is the specification of the JWT implementation. If you need to have some sort of refresh token, that case you need to consider a storage. Storage can be anything like REDIS, MongoDB or any other SQL based DB. For further clarification about JWT in 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

Carbineer answered 24/7, 2018 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.