node cluster with socket.io and expressjs - node.js

Here's my server code:
I'm trying to use cluster with socket.io and expressjs. I'm testing this on my quad core desktop.
var cluster = require('cluster')
var numCPUs = require('os').cpus().length
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork()
}
console.log(new Date());
}
else {
// get required modules
var connect = require('connect');
var express = require('express');
var mongo = require('mongodb');
var MongoStore = require('connect-mongo')(express);
var server;
var redis = require('redis');
var http = require('http');
// create server object
app = exports.module = express();
function configureServer() {
//configure environments
app.configure('production', function() {
app.use(express.errorHandler());
});
//configure server instance
app.configure(function() {
app.use(express.compress())
// set jade as default view engine
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.cookieParser("refdgfd"));
app.use(express.methodOverride());
app.use(app.router);
// use express logger
});
loadModules();
}
var server = http.createServer(app).listen(80);
socket = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis')
, pub = redis.createClient()
, sub = redis.createClient()
, client = redis.createClient();
socket.set('store', new RedisStore({
redisPub : pub
, redisSub : sub
, redisClient : client
}));
socket.configure(function() {
socket.set('log level', 1);
socket.set('transports', ['websocket']);
});
configureServer();
}
Is there anything that stands out to be really wrong?
The logs indicate that everything happens 4 times. Which seems correctly. But my socket.io's channel users also indicate that whenever I connect to a page, I connect 4 times, (create 4 user sockets). That seems wrong. How do I fix it and are there other things wrong with my code. Thanks.

At this moment socket.io has no full support for node.js cluster. It works fine for me with websocket transport, but xhr-pooling fails. You can reed more in this issues: #939, #917. The problem is the xhr-pooling request can get into the thread different from the one which it was originally authorized.

Related

Issue with a 404 error using socket.io

I've recently gotten into socket.io, during a long-term project of mine. Which is probably why I am having such a hard time of it, because their "getting started" sections don't take into account you may already be deep into development of your own application. The main issue is it connecting, it won't do it, client-side that is.
I keep getting a 404 not found which is cause by CANNOT POST /socket.io/ Which it is right, it can't obviously, mainly because that is not where the socket.io location is (it is in node_modules per usual). Secondly if I create a route for this, it does absolutely nothing. So here is code initializing it:
/*jshint esversion: 6*/
const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const path = require('path');
const expressValidator = require('express-validator');
const flash = require('connect-flash');
const session = require('express-session');
const passport = require('passport');
const db = require('./config/db');
// Init App
const app = express();
// Init http server
const server = http.createServer(app);
// init socket
const io = require('socket.io').listen(server);
Here is the clientside trying to connect to it:
if (window.location.hostname == 'playkog.net' || window.location.hostname == 'www.playkog.net') {
var port = 443;
} else {
var port = 8080;
}
var connected = false;
var socket = io.connect(window.location.hostname + ':' + port, { 'connect timeout': 5000 });
// Connection Successful
socket.on('connect', function () {
console.log('a user connected');
connected = true;
});
socket.on('disconnect', function () {
console.log('user disconnected');
connected = false;
});
I imagine I'll have to connect to a different port, however I'm not sure which, nor if that is my issue (or only issue). Of course be extremely new to this kind of stuff (amateur at best) some of these things just go right over my head.
Here is a screenshot of my console

socket.io not emitting to all clients

I'm running into a strange issue where - in my production environment only (everything works fine in local testing) - socket.emit() works fine but io.emit() does not. In other words, each individual socket client connection can send and receive messages to the server, but when the server emits to all clients, none of them receive the message. This is strange, because it can see all the clients - if I check Object.keys(io.engine.clients) I see the ids of all connected clients. But io.emit() doesn't broadcast to any of them.
I'm using the latest version of node (7.7.4 and socket.io (1.7.3). I'm hosting on Heroku, and have enabled sticky sessions. I use cluster so that all CPUs are utilized, and I have a redis service set up to synchronize all the workers. All of that infrastructure appears to be working just fine.
Why would socket.emit() work for any given socket, but none of the other methods?
socket.on('sendChat', function(messageBundle) {
console.log('sockets: ' + Object.keys(io.engine.clients)) //shows clients
io.sockets.emit('incomingChat', messageBundle); //nothing
io.emit('incomingChat', messageBundle); //also nothing
var clients = Object.keys(io.engine.clients);
for (var i = 0; i < clients.length; i++) {
console.log('broadcasting to: ' + clients[i]);
socket.broadcast.to(clients[i]).emit('incomingChat', messageBundle); //still nothing
}
socket.emit('incomingChat', messageBundle); //this works though
});
UPDATE:
Here is where I define the socket stuff earlier.
var redis = require('redis');
var pub = redis.createClient(redisPort, redisHost, {
auth_pass: redisPwd
});
var sub = redis.createClient(redisPort, redisHost, {
detect_buffers: true,
auth_pass: redisPwd
});
var adapter = require('socket.io-redis');
io.adapter(adapter({
pubClient: pub,
subClient: sub
}));
var cluster = require('cluster');
var WORKERS = process.env.WEB_CONCURRENCY || 3;
if (cluster.isMaster) {
for (var i = 0; i < WORKERS; ++i)
console.log('forking process ' + i);
cluster.fork();
} else {
var express = require('express');
var session = require('express-session');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var port = process.env.PORT || 5000;
server.listen(port, function() {
console.log("Listening on " + port);
console.log("testing");
});
var mySession = session({...}) //this enables me to authenticate the socket using the same session middleware as the express routes
io.use(function(socket, next) {
mySession(socket.handshake, {}, next);
});
io.on('connection', function(socket) {
socket.emit('welcome');
console.log('socket connection');
socket.on(... etc.)
...})
...})
On the client side, the socket connection is initiated using a simple config:
var socket = io({
reconnection: true,
reconnectionDelay: 100,
});
io is undeclared on the beginning, and then You try to redeclare it in the else statement.
For me the issue was mismatching version of socket.io and reddis-adapter (upgraded socket io to 3.0.1 and redis adapter remained at 5.x.x)
Upgrading adapted to 6.0.1 solved it

Adding Socket.io to an Express 4 Server - multiple file setup

I've been bouncing back and forth between socket.io and express.io - but settled for socket.io with Express 4, as I would like to use Namespaces.
I have worked on some examples of having an Express 4 Server using Socket.io - but most examples are based on one file with everything in it. I am trying to separate all my code to make it easier but I am at a loss as to how to add Socket.io (or where).
I have index.js which uses Cluster and basically calls server.js:
var server = require( "./server.js" );
var cluster = require('cluster');
var webApp={
run: function(){
console.log('Starting: Server');
server.listen();
}
};
if(cluster.isMaster){
cluster.fork();
cluster.on('exit',function(worker){
console.log('Worker ' + worker.id + ' died..');
setTimeout( function () { cluster.fork(); }, 1000 );
});
} else{
try {
webApp.run();
}
catch(e)
{
console.log(e);
process.exit(1);
}
process.on('uncaughtException', function(err){
console.log(err);
process.exit(1);
});
process.on( 'SIGINT', function () {
console.log( "\n SIGINT (Crtl-C)" );
//Kill worker
cluster.disconnect();
process.exit(1);
});
}
This then calls the server.js file:
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var config = require('./config/config.js');
var router = require('./routes');
var Server = Object.subClass({
/**
* Constructor
*/
init:function(){
this.appServer = express();
var that = this;
var appServer = this.appServer;
appServer.use(express.static(__dirname + '/public'));
appServer.set('views', path.join(__dirname, 'views'));
appServer.set('view engine', 'ejs');
appServer.use(bodyParser.urlencoded({ extended: true }));
appServer.use(bodyParser.json());
appServer.get('/',router.root);
},
/**
* Listener HTTP
*/
listen:function(){
var port = config.rest.port;
console.log(':: on port:' + port);
this.appServer.listen(port);
}
});
module.exports = new Server();
I am only having one 'route', which is the '/' and is defined in routes.js file. The page loads fine but where do I add the server side socket.io? and do I add any socket.io namespace definitions in the routes.js file or in the javascript of the page being loaded?
There are so many ways of using sockets that I can't seem to work out the best approach for my multi-file approach.
Any help would be brilliant as I seem to be going in circles.
Enjoy our Saturday :)
Thanks again.
I've spent the morning looking at the Cluster/Worker approach and decided to use 'SocketCluster' as it seems to do what I need.
Enjoy your Sunday

How to connect Two Socket.io Node Application using Socket.io-client in Node js

In my application i need to connect two socket.io node applications.Using socket.io-client we can do like this.But i don't know how socket.io-client works and where to include that.
First Node Application
var express = require('express')
, http = require('http');
var app = express();
app.use(function (req, res) {
app.use(express.static(__dirname + '/public'));
});
var server = http.createServer(app);
var io = require('socket.io').listen(server);
server.listen(3000);
io.sockets.on('connection',function(socket){
socket.on('eventFiredInClient',function(data){
socket.emit('secondNodeAppln',data);// i need to get this event in my 2nd node application how can i do this by using socket.io-client
});
});
Second Node Application
var express=require('express');
var http=require('http');
var app=express();
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
var server = http.createServer(app);
var serverAddress = '127.0.0.1';
var serverPort = 3000; //first node appln port
var clientio = require('socket.io-client');
var socket = clientio.connect(serverAddress , { port: serverPort });
socket.on('connect', function(){
console.log('connected');
});
socket.on('disconnect', function(){
console.log('disconnected');
});
var io = require('socket.io').listen(server);
server.listen(6509);
//here i need to get the 'secondNodeAppln' event raised in first node application.How can i do this.
You need to create a socket.io client in your first app:
var io = require('socket.io').listen(server); // this is the socket.io server
var clientio = require('socket.io-client'); // this is the socket.io client
var client = clientio.connect(...); // connect to second app
io.sockets.on('connection',function(socket) {
socket.on('eventFiredInClient',function(data) {
client.emit('secondNodeAppln', data); // send it to your second app
});
});
And in your second app, just listen for those events:
io.sockets.on('connection', function (socket) {
socket.on('secondNodeAppln', function(data) {
...
});
});
There's a bit of a race condition because the code above doesn't wait for a connect event on the client socket before passing events to it.
EDIT see this gist for a standalone demo. Save the three files to a directory, start the servers:
node serverserver &
node clientserver
And open http://localhost:3012 in your browser.

Node.js,Socket.io,Http-Proxy,Cluster,Express - Handshake drops on disconnect - delay in reconnect

I'm using node.js and socket.io. I'm using http-proxy to use port 80 on a machine that's running apache also. Apache is on a different IP. This all works great.
I added in Cluster and things got funky. It spawns two workers (dual core VM) as expected. But the connection on the client side, is off. Sometimes it's good, then if you disconnect, there's a delay reconnecting (there wasn't without the Cluster).
Here's the code I have.. any ideas why there's the delay between disconnect and connect using the cluster?
var http = require('http'),
httpProxy = require('http-proxy'),
io = require('socket.io'),
cluster = require('cluster'),
express = require('express'),
RedisStore = require('connect-redis')(express);
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 {
//Server for workers
var app = express.createServer().listen(8585, '172.16.183.129');
app.configure(function(){
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.session({ secret: "secret", store: new RedisStore}));
app.use(app.router);
app.use(express.errorHandler({showStack: false, dumpExceptions: false}));
app.use(express.static(__dirname + '/public'));
});
//Create proxy server to use port 80
var proxy = httpProxy.createServer(8585, '172.16.183.129');
proxy.listen(80, '172.16.183.129');
//Initilize socket.io
var io = require('socket.io').listen(app, {origins: '*:*'});
io.sockets.on('connection', function(socket){
var test = setInterval(function() {
socket.emit('test', { result: numCPUs});
i++;
}, 900);
socket.on('disconnect', function () {
console.log('disconnect');
clearInterval(auctionTimer);
});
});
}
You need to add in a RedisStore so that the socket workers can work together. Note that the latest version of socket.io has a built in RedisStore that you probably should use instead of the connect-redis stuff.
socketio = require('socket.io')
var io = socketio.listen(app, {origins: '*:*'});
var host = 'redisserver'
var opts = {redisSub:{host:host},redisPub:{host:host},redisClient:{host:host}}
io.set('store', new socketio.RedisStore(opts))
As rbrc, you need some method for the different socket.io servers in your workers to communicate and share data, otherwise when you connect to one and then the other, they can't know that you already have a session. This goes for express' sessions too, and I see you use RedisStore there. Since you have a dependency on redis already, socketio.RedisStore seems like a good fit, otherwise you could try socket.io-clusterhub.

Resources