Socket.io and Redis behind a load balancer. - node.js

I need socket.io to work behind a load balancer. I know, its a common question. Ive implemented:
var io = require('socket.io').listen(app.listen(cfg.port, function() {
cfg.logger.info("Express Server listening on "+cfg.port+" for env "+cfg.environment);
}));
var adapter = require('socket.io-redis');
var pubClient = redis.createClient(cfg.redisPort, cfg.redisHost, {auth_pass: cfg.redisPW, return_buffers: true});
var subClient = redis.createClient(cfg.redisPort, cfg.redisHost, {auth_pass: cfg.redisPW, return_buffers: true});
io.adapter(adapter({pubClient: pubClient, subClient: subClient}));
This seems to connect fine to my redis store. I guess my question is though, is this all that needs to be implemented in order for socket.io to work with the load balancers? How would i even go about testing that this is working correctly with Elastic Beanstalk?

Related

WebSocket is closed before the connection is established. Socket.io and React

I want to make an Chat application with Socket.io and I've followed this tutorial: https://youtu.be/ZwFA3YMfkoc. I am using React and Node.js
Everything works fine while using it locally and even on different devices on my Network. However if I am hosting my Backend on Heroku it doesn't work.
The error Message is:
WebSocket connection to 'URL' failed: WebSocket is closed before the connection is established.
(URL is the URL of my Backend with the Port). I am using SSL.
I've already tried to enable session affinity but it already was enabled.
My backend code is: (atleast the code that I think is important to the Problem)
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
server.listen(PORT, () => console.log("Server started on " + PORT));
My frontend code is written in React and is:
var connectionOptions = {
"force new connection": true,
reconnectionAttempts: "Infinity",
timeout: 10000,
transports: ["websocket"],
};
const ENDPOINT = "URL";
socket = io(ENDPOINT, connectionOptions);
So I've fixed my Problem.
The URL on the client side had to be without the Port.
So for example:
const ENDPOINT = "https://web.site.com";
and not:
const ENDPOINT = "https://web.site.com:1337";
Call socket.connect() or just add autoconnect: true in options you are providing.

How to make 2 nodejs servers connect with SSL websocket?

I am trying to make 2 servers communicate via socket.io library and SSL.
This used to work until an upgrade of socket.io package (can't tell you which).
I have managed to fix secure connection with a browser. I have also made it work between unsecure (http) servers. But the secure (https) servers refuse to connect between themselves. You may argue that socket.io is not made for server to server communications, but it would save me lots of work to fix it.
I am now running:
node: 7.5.0
express: 4.16.2
socket.io (and socket.io-client): 2.0.3
I cannot even make simple examples below work (removing all my middleware).
node server
// Use SSL certificate
const cert_path = "..";
const fs = require('fs');
const https_options = {
key: fs.readFileSync(cert_path+'/privkey.pem'),
cert: fs.readFileSync(cert_path+'/cert.pem')
};
const app = require('express')();
const https = require('https');
const server = https.createServer(https_options, app);
const io = require('socket.io')(server);
server.listen(8000);
io.on('connection', function (socket) {
console.log("connected");
});
node client
const io = require('socket.io-client');
const socket = io.connect(
'https://localhost:8000',
{secure: true}
);
socket.on("connect", function () {
console.log("connected");
});
Nothing happens, none of them connect. Any idea why?
EDIT: I'm getting both connect_error and reconnect_error that pop every 5s on client side:
{ Error: xhr poll error
at XHR.Transport.onError (../node_modules/engine.io-client/lib/transport.js:64:13)
at Request.<anonymous> (../node_modules/engine.io-client/lib/transports/polling-xhr.js:128:10)
at Request.Emitter.emit (../node_modules/component-emitter/index.js:133:20)
at Request.onError (../node_modules/engine.io-client/lib/transports/polling-xhr.js:310:8)
at Timeout._onTimeout (../node_modules/engine.io-client/lib/transports/polling-xhr.js:257:18)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5) type: 'TransportError', description: 503 }
Digging further in the errors, I see it may come from the certificate. But while I apply several workarounds of SO, I'm getting consecutively ECONNREFUSED, UNABLE_TO_VERIFY_LEAF_SIGNATURE, and finally DEPTH_ZERO_SELF_SIGNED_CERT...
After trying hard:
re-generate my Let's Encrypt certificate
re-generate my self-signed certificates (openssl) and use them by server+client
tinker with socket.io connect options (secure, rejectUnauthorized, ..)
tinker with nodejs global setup even (process.env['NODE_TLS_REJECT_UNAUTHORIZED'])
I finally stumbled on this page of github. It solved my issue and it's worth sharing it.
node client
const https = require('https');
https.globalAgent.options.rejectUnauthorized = false;
const io = require('socket.io-client');
const sockets = io.connect('https://localhost:8001', {agent: https.globalAgent});
Even if I would have preferred getting my connection authorized in the first place, this will work for me.

Socket io cluster on same port

I need to cluster socket.IO server and want to use same port for all connections
In order to do it I create express server, configure it and then tell socket.IO to listen to this server
if(cluster.isMaster){
var cpuCount = require('os').cpus().length;
for (let i = 0; i < cpuCount; i += 1) {
cluster.fork();
} else {
const credentials = {key: privateKey, cert: certificate};
const app = express();
const httpsServer = https.createServer(credentials, app);
httpsServer.listen(8003);
const io = SocketIO.listen(httpsServer);
}
But this doesn't work for multiple servers. When I'm trying to connect to socket server I get following message:
websocket.js?13d9:112 WebSocket connection to 'wss://...' failed: Error during WebSocket handshake: Unexpected response code: 400
But in server side event listener on("connection", () => {}) works.
I have same code for creating server clusters when I render app and it works just fine.
Socket server works when there's only one socket.IO server listening to this port.
If there's a way to create multiple socket.IO listeners for one port in cluster?
Finally found the answer
Socket.io is doing multiple requests to perform handshake and establish connection with a client. With a cluster those requests may arrive to different workers, which will break handshake protocol.
So I decided to use sticky session lib for socket.IO and it works exactly as I wanted

socket.io-redis giving error on heroku

i am trying to deploy my app on heroku.i added redistogo addon in my app.it is based on node.js and express.
i write a small code
var redis = require('socket.io-redis');
var io = require('socket.io')(server);
io.adapter(redis(process.env.REDISTOGO_URL));
but on last line i am getting error:
Error: Redis connection to redistogo:6379 failed - getaddrinfo ENOTFOUND redistogo
can any one help why i am facing this error and get rid of this error.6379 is default port but my redistogo url doesn't has 6379 port no.it's port no is 10281.
Is this a bug in socket.io-redis module or i am doing something wrong ??
If your Redis is running on port 10281 you need to set it when initializing adapter.
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: process.env.REDISTOGO_URL, port: 10281 }));
Check out documentation https://github.com/automattic/socket.io-redis#adapteropts
If redis DB has a password then it's better to opt for
var redis = require('redis').createClient;
var adapter = require('socket.io-redis');
var pub = redis(port, host, { auth_pass: "pwd" });
var sub = redis(port, host, { detect_buffers: true, auth_pass: "pwd"
});
io.adapter(adapter({ pubClient: pub, subClient: sub }));
in case of heroku enter host as
redis://redistogo:XXXXXXXXX#beardfish.redistogo.com
and port : provided in redistogo_url
and now it's working great.

Scaling to 2+ dynos on Heroku with socket.io-redis and RedisToGo

I'm trying to use socket.io-redis to scale my app on Heroku to 2 dynos (or more). Here is my code (where config.redis is just an object housing RedisToGo port, host, and pass values):
var redisApp = require('redis');
var redis = require('socket.io-redis');
if(process.env.NODE_ENV === 'production') {
var socketpub = redisApp.createClient(config.redis.port, config.redis.host, {auth_pass: config.redis.pass, return_buffers: true});
var socketsub = redisApp.createClient(config.redis.port, config.redis.host, {auth_pass: config.redis.pass, detect_buffers: true});
var client = redisApp.createClient(config.redis.port, config.redis.host, {auth_pass: config.redis.pass, return_buffers: true});
socketio.adapter(redis({
pubClient: socketpub,
subClient: socketsub,
redisClient: client
}));
}
On the client side I have:
var ioSocket = io('', {
path: '/socket.io-client',
'force new connection': true,
transports: ['websocket']
});
..so socket.io doesn't try to use polling.
I also have the right Heroku env vars configured for RedisToGo (REDISTOGO_HOST,
REDISTOGO_PASS, REDISTOGO_PORT).
When we're scaled to 1 dyno, the socket behavior is perfect. At 2 dynos, the behavior is way off - requests are being randomly made to either 1 dyno or the other, and the socket events being emitted are sent only to clients running on the dyno to which the request was made and not all (which socket.io-redis & RedisToGo should be taking care of).
Any thoughts would be greatly appreciated!
not sure if this helps you but I am using in this way redis and socketio and it is working good.
var redis = require('redis').createClient;
var adapter = require('socket.io-redis');
var port = config.redistogo.port;
var host = config.redistogo.host;
var pub = redis(port, host, {
auth_pass: auth_pass
});
var sub = redis(port, host, {
detect_buffers: true,
auth_pass: auth_pass
});
io.adapter(adapter({
pubClient: pub,
subClient: sub
}));

Resources