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

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

Related

Create Websocket Secure on a HTTPS web server in express app

I have a application in express.js. Am unable to create wss on a HTTPS web server.
var fs = require('fs');
var express = require('express');
var app = express();
var cfg = {
ssl: true,
port: 8001,
ssl_key: 'sslcert/ssl.key',
ssl_cert: 'sslcert/ssl.crt'
};
var httpServ = (cfg.ssl) ? require('https') : require('http');
if (cfg.ssl) {
httpsServer = httpServ.createServer({
key: fs.readFileSync(cfg.ssl_key),
cert: fs.readFileSync(cfg.ssl_cert)
}, app)
.listen(cfg.port, function () {
console.log('Magic happening at https://Secure');
});
} else {
httpsServer = httpServ.createServer(app)
.listen(cfg.port, function () {
console.log('Magic happening at http://InSecure');
});
}
var WebSocketServer = require('websocket')
.server;
var wsServer = new WebSocketServer({
httpServer: httpsServer
});
wsServer.on('connection', function connection(ws) {
console.log('Socket Connected');
});
I'm under the impression that passing a reference of the web server to WebSocket, then WebSocket would know the port and SSL capabilities of the server. But every time I get an error in the console
WebSocket connection to 'ws://localhost:8001/?trackme=TUKOCVPAF' failed: Connection closed before receiving a handshake response
How can I create a wss://localhost:8001/?trackme=TUKOCVPAF' when creating the Websocket..??

Socket.io and Redis behind a load balancer.

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?

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.

no method 'adapter' in 'io' (node.js, heroku, RedisCloud, socket.io)

Using RedisCloud on Heroku with node.js to allow (future) scaling to more than one dyno.
Got Redis working by following this:
https://devcenter.heroku.com/articles/rediscloud#using-redis-from-node-js
For example the line below with the comment "Prints 'bar'" does write 'bar' to the console.
Then followed this to add socket.io-redis for scaling:
https://github.com/Automattic/socket.io-redis
The above should allow me to use something like the following:
io.adapter(redis({ host: 'localhost', port: 6379 }));
The equivalent in my code below is:
io.adapter(ioredis (redisOptions));
However, I kept getting an error saying that io had no method 'adapter'.
Or if I checked first with an 'if' statement to see if io.adapter existed (as shown in code below), then the conditional code never executed.
Can anyone see what I'm doing wrong here? Why doesn't io.adapter exist?
in package.json
"dependencies": {
"redis": "^0.12.1",
"express": "^3.4.8",
"socket.io": "^0.9.16",
"socket.io-redis":"^0.1.4"
}
also tried adding this to package.json
"socket.io-adapter": "^0.2.0"
in app.js (my node server code):
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server)
, redis = require('redis')
, ioredis = require('socket.io-redis')
, url = require('url')
, redisURL = url.parse(process.env.REDISCLOUD_URL);
And later in app.js :
io.sockets.on('connection', function (socket) {
var pub1 = redis.createClient(redisURL.port, redisURL.hostname, {return_buffers: true});
var sub1 = redis.createClient(redisURL.port, redisURL.hostname, {return_buffers: true});
pub1.auth(redisURL.auth.split(":")[1]);
sub1.auth(redisURL.auth.split(":")[1]);
var redisOptions = {
pubClient: pub1,
subClient: sub1,
host: redisURL.hostname,
port: redisURL.port
};
pub1.set('foo', 'bar');
sub1.get('foo', function (err, reply) {
console.log("redis test : "+reply.toString());
// Prints 'bar'
});
if (io.adapter) {
// Never reached
io.adapter(ioredis (redisOptions));
console.log ("mylog: io.adapter found");
}
console.log ("mylog server connection event fired");
socket.io-redis is meant to be used with socket.io 1.x.
The 0.9.x version doesn't have the adapter.

redis session not working on server

I am using redis for session in my node.js express app. It works fine on my dev box, but on production, it seems redis sessions are not being saved.
I'm not seeing any kind of error, other than I cannot login.
Redis is running w/ same configuration. But when I run redis-cli and type 'select 1' (the db) and KEYS '*' I get nothing.
var RedisStore = require('connect-redis')(express);
app.use(express.session({
store: new RedisStore({
host: cfg.redis.host,
db: cfg.redis.db
}),
secret: 'sauce'
}));
cfg.redis.host is localhost
and cfg.redis.db is 1
This is the error I get when I run redis-cli monitor
Error: Protocol error, got "s" as reply type byte
A few suggestions. Are you sure Redis uses the same port and password in production? If you're using SSL with a service like Heroku, you need to set proxy: true to have Express treat cookies that arrive after after earlier SSL termination.
.use(express.session({
store: new RedisStore({
port: config.redisPort,
host: config.redisHost,
db: config.redisDatabase,
pass: config.redisPassword}),
secret: 'sauce',
proxy: true,
cookie: { secure: true }
}))
I require the following config.js file to pass on Redis config values:
var url = require('url')
var config = {};
var redisUrl;
if (typeof(process.env.REDISTOGO_URL) != 'undefined') {
redisUrl = url.parse(process.env.REDISTOGO_URL);
}
else redisUrl = url.parse('redis://:#127.0.0.1:6379/0');
config.redisProtocol = redisUrl.protocol.substr(0, redisUrl.protocol.length - 1); // Remove trailing ':'
config.redisUsername = redisUrl.auth.split(':')[0];
config.redisPassword = redisUrl.auth.split(':')[1];
config.redisHost = redisUrl.hostname;
config.redisPort = redisUrl.port;
config.redisDatabase = redisUrl.path.substring(1);
console.log('Using Redis store ' + config.redisDatabase)
module.exports = config;

Resources