Reputation: 15872
I've built an application which uses Express and Socket.io, built on Node.js. I'd like to incorporate clustering to increase the amount of requests that my application can process.
The following causes my application to produce a socket handshake error...
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
});
} else {
app.listen(3000);
}
A console log shows that socket.io is being started up multiple times.
jack@jack:~$ node nimble/app.js
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
info - socket.io started
Socket.io is currently being set up in the top of my server code using:
var io = require('socket.io').listen(app);
The websocket authorization code:
//Auth the user
io.set('authorization', function (data, accept) {
// check if there's a cookie header
if (data.headers.cookie) {
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
//Save the session store to the data object
data.sessionStore = sessionStore;
sessionStore.get(data.sessionID, function(err, session){
if(err) throw err;
if(!session)
{
console.error("Error whilst authorizing websocket handshake");
accept('Error', false);
}
else
{
console.log("AUTH USERNAME: " + session.username);
if(session.username){
data.session = new Session(data, session);
accept(null, true);
}else {
accept('Invalid User', false);
}
}
})
} else {
console.error("No cookie was found whilst authorizing websocket handshake");
return accept('No cookie transmitted.', false);
}
});
Console log of the error message:
Error whilst authorizing websocket handshake
debug - authorized
warn - handshake error Error
Upvotes: 2
Views: 3270
Reputation: 39223
What is your sessionStore
? If it's the MemoryStore
shipped with connect
, then sessions won't be shared between your workers. Each worker will have it's own set of sessions, and if a client connects to another worker, you wont find their session. I suggest you take a look at e.g. connect-redis for sharing sessions between processes. Basic usage:
var connect = require('connect')
, RedisStore = require('connect-redis')(connect);
var app = connect.createServer();
app.use(connect.session({store: new RedisStore, secret: 'top secret'});
Of course, this requires that you also set up redis. Over at the Connect wiki there are modules for CouchDB, memcached, postgres and others.
This only solves part of your problem, though, because now you have sessions shared between workers, but socket.io
has no way of sending messages to clients which are connected to other workers. Basically, if you issue a socket.emit
in one worker, that message will only be sent to clients connected to that same worker. One solution to this is to use RedisStore
for socket.io
, which leverages redis to route messages between workers. Basic usage:
var sio = require('socket.io')
, RedisStore = sio.RedisStore
, io = sio.listen(app);
io.set('store', new RedisStore);
Now, all messages should be routed to clients no matter what worker they are connected to.
See also: Node.js, multi-threading and Socket.io
Upvotes: 2