How to reuse redis connection in socket.io? - node.js

Here is my code using socket.io as WebSocket and backend with pub/sub redis.
var io = io.listen(server),
buffer = [];
var redis = require("redis");
var subscribe = redis.createClient(); **<--- open new connection overhead**
io.on('connection', function(client) {
console.log(client.request.headers.cookie);
subscribe.get("..", function (err, replies) {
});
subscribe.on("message",function(channel,message) {
var msg = { message: [client.sessionId, message] };
buffer.push(msg);
if (buffer.length > 15) buffer.shift();
client.send(msg);
});
client.on('message', function(message){
});
client.on('disconnect', function(){
subscribe.quit();
});
});
Every new io request will create new redis connection. If someone open browser with 100 tabs then the redis client will open 100 connections. It doesn't look nice.
Is it possible to reuse redis connection if the cookies are same?
So if someone open many browser tabs also treat as open 1 connection.

Actually you are only creating a new redis client for every connection if you are instantiating the client on the "connection" event. What I prefer to do when creating a chat system is to create three redis clients. One for publishing, subscribing, and one for storing values into redis.
for example:
var socketio = require("socket.io")
var redis = require("redis")
// redis clients
var store = redis.createClient()
var pub = redis.createClient()
var sub = redis.createClient()
// ... application paths go here
var socket = socketio.listen(app)
sub.subscribe("chat")
socket.on("connection", function(client){
client.send("welcome!")
client.on("message", function(text){
store.incr("messageNextId", function(e, id){
store.hmset("messages:" + id, { uid: client.sessionId, text: text }, function(e, r){
pub.publish("chat", "messages:" + id)
})
})
})
client.on("disconnect", function(){
client.broadcast(client.sessionId + " disconnected")
})
sub.on("message", function(pattern, key){
store.hgetall(key, function(e, obj){
client.send(obj.uid + ": " + obj.text)
})
})
})

Redis is optimized for a high level of concurrent connections. There is also discussion about multiple database connections and connection pool implementation in node_redis module.
Is it possible to reuse redis
connection if the cookies are same? So
if someone open many browser tabs also
treat as open 1 connection.
You can use for example HTML5 storage on the client side to keep actively connected only one tab and others will handle communication/messages through storage events. It's related to this question.

I had this exact problem, with an extra requirement that clients must be able to subscribe to private channels, and publish to those channels should not be sent to all listeners. I have attempted to solve this problem by writing a miniature plugin. The plugin:
Uses only 2 redis connections, one for pub, one for sub
Only subscribes to "message" once total (not once every redis connection)
Allow clients to subscribe to their own private channels, without messages being sent to all other listening clients.
Especially useful if your prototyping in a place where you have a redis connection limit (such as redis-to-go).
SO link: https://stackoverflow.com/a/16770510/685404

You need to remove the listener when client disconnect.
var io = io.listen(server),
buffer = [];
var redis = require("redis");
var subscribe = redis.createClient();
io.on('connection', function(client) {
console.log(client.request.headers.cookie);
subscribe.get("..", function (err, replies) {
});
var redis_handler = function(channel,message) {
var msg = { message: [client.sessionId, message] };
buffer.push(msg);
if (buffer.length > 15) buffer.shift();
client.send(msg);
};
subscribe.on("message", redis_handler);
client.on('message', function(message){
});
client.on('disconnect', function(){
subscribe.removeListerner('message', redis_handler)
//subscribe.quit();
});
});
See Redis, Node.js, and Socket.io : Cross server authentication and node.js understanding

Using redis as a store has become much simpler since this question was asked/answered. It is built in now.
Note, that if you are using redis because you are using the new node clustering capabilities (utilizing multiple CPUs), you have to create the server and attach the listeners inside of each of the cluster forks (this is never actually explained anywhere in any of the documentation ;) ). The only good code example online that I have found is written in CoffeeScript and I see a lot of people saying this type of thing "just doesn't work", and it definitely doesn't if you do it wrong. Here's an example of "doing it right" (but it is in CoffeeScript)

Related

Send a message from client to server on connection node.js

I want my client-side code to send the server the user's userid when establishing the connection, then i want the server to check the database for new messages for each user that is connecting, and send the user the number of new messages it has when new messages are available.
My client-side code:
var socket = io.connect('http://localhost:8000');
socket.on('connect', function () {
socket.emit('userid', '1');
});
socket.on('new_message', function (data) {
var number_of_messages= "<p>"+data.number+"</p>";
$('#container').html(number_of_messages);
});
My server-side code:
io.sockets.on( 'userid', function (data) {
console.log('userid: '+data);
});
My problem is that the above code is not working: the userid is never received by the serverside and the on('userid') is never called.
My question is how to know which socket sent this user id and how to send to only this specific socket a certain message.
I have solved the problem by saving the clients socket and their id into a global array. this is not a good solution but it works; I know there are rooms and namespaces but I never used it..
socket.io namespaces and rooms
however,
(I used express)
client:
var socket = io.connect('http://localhost:3000',{reconnection:false});
socket.once('connect', function() {
socket.emit('join', '#{id}');
};
server:
var clients = [];
app.io.on('connection', function(socket) {
socket.on('join', function(data) {
clients.push({
ws: socket,
id: data
});
//retrive the messages from db and loop the clients array
//and socket.send(things)
}
}

How to send Redis message to client side when a new message is published in that channel

I am trying to implement a functionality wherein when a user is viewing a page, he should be automatically subscribed a Redis channel unique to that user. And all message published to that Redis channel should be received by the client side.
Below is by client side code,
var socket = io.connect('localhost:5000');
socket.on('connect', function(){
socket.emit("subscribe",$("#app_id").text());
});
socket.on('message', function(message) {
$("#lst").append("<li>" + message + "</li>")
});
And on the nodejs server, I have below code,
var client = redis.createClient();
io.sockets.on('connection', function (socket) {
socket.on("subscribe",function(channel){
client.subscribe(channel);
socket.join(channel);
});
client.on("message", function(channel, message){
socket.broadcast.to(channel).emit('message', message);
});
});
When I open the client page in two separate browsers, its subscribing to the two channels correctly and message published to those Redis channels are shown at client side. But if I refresh the page, the number of messages which I get is double and if I refresh again, it adds up. I guess the listeners getting added-up on each refresh.
I have coded the Redis listener inside the socket.io connection listener because I want to emit the message to the socket.This may be cause of the problem am facing. Dono how else to do it as I need socket instance to send message to client side. Please help on this.
as you already told: remove the listener on disconnect.
Have a look at how-to-remove-redis-on-message-listeners and socket-io-handling-disconnect-event for further reading.
untested sample
var client = redis.createClient();
io.sockets.on('connection', function (socket) {
var broadcast = function(channel, message){
socket.broadcast.to(channel).emit('message', message);
}
socket.on("subscribe",function(channel){
client.subscribe(channel);
socket.join(channel);
});
socket.on('disconnect', function() {
client.unsubscribe(channel);
client.removeListener(broadcast)
})
client.on("message", broadcast);
});

Using Socket.io, how do I detect when a new channel/room has been created?

From the server, I want to be able to detect when a client creates new a room or channel. The catch is that the rooms and channels are arbitrary so i don't know what they will be until the user joins/subscribes. Something like this:
Server:
io.on('created', function (room) {
console.log(room); //prints "party-channel-123"
});
Client:
socket.on('party-channel-123', function (data) {
console.log(data)
})
I also can't have any special client requirements such as sending a message when the channel is subscribed as such:
socket.on('party-channel-123', function (data) {
socket.emit('subscribed', 'party-channel-123');
})
Server:
io.sockets.on('connection', function(socket) {
socket.on('createRoom', function(roomName) {
socket.join(roomName);
});
});
Client
var socket = io.connect();
socket.emit('createRoom', 'roomName');
the io object has references to all currently created rooms and can be used as such:
io.sockets.in(room).emit('event', data);
Hope this helps.
PS. I know its emitting the 'createRoom' that you probably don't want but this is how socket.io is used, this is pretty much copy/paste out of the docs. There are tons of examples on the socket.io website and others.

socket.io is disconnecting all other connected users in my node twitter app

When I open two browser windows in incognito mode (different sessions).
I get a stream for one, which works fine...new tweets come in as expected from the twitter stream api. The problem is that when the other window loads the page, the first incognito window gets disconnected.
Here is the code I'm using. I'm not sure if twitter might not allow simultaneous streams or if its something with my logic.
var mod = module.exports = {}
, twitter = require('twitter')
, c = console;
var io
, sock
, intv = {};
function onNewsInit(data){
setTimeout(function(){
stream(streamQuery);
}, 1000 * 5);
}
function stream(query){
c.log('stream query', query);
twit.stream('statuses/filter', {track:query}, function(stream) {
stream.on('data', function (data) {
c.log(data);
var item = getItem(query, data);
c.log('stream item', item);
if ( item ) {
sock.emit('twitter:item', { item: item });
}
});
stream.on('end', function (response) {
// Handle a disconnection
c.log('stream end');
});
stream.on('destroy', function (response) {
// Handle a 'silent' disconnection from Twitter, no end/error event fired
c.log('stream destroy');
});
// Disconnect stream after five seconds
sock.on('news:end', stream.destroy);
sock.on('disconnect', stream.destroy);
});
}
mod.register = function register(soc, sio) {
c.log('news register');
io = sio;
sock = soc;
sock.on('news:init', onNewsInit);
sock.on('news:end', onNewsEnd);
};
app.js
io.sockets.on('connection', function(sock){
news.register(sock, io);
});
Is there any reason one client would be able to disconnect the other?
It turns out the twitter stream API only allows one simultaneous connection.
As described here:
https://dev.twitter.com/docs/streaming-apis/streams/public
Connections
Each account may create only one standing connection to the public
endpoints, and connecting to a public stream more than once with the
same account credentials will cause the oldest connection to be
disconnected.
Clients which make excessive connection attempts (both successful and
unsuccessful) run the risk of having their IP automatically banned.

What is the proper way to manage multiple chat rooms with socket.io?

What is the proper way to manage multiple chat rooms with socket.io?
So on the server there would be something like:
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
socket.broadcast.emit('receive', data);
});
});
Now this would work fine for one room, as it broadcasts the message out to all who are connected. How do you send messages to people who are in specific chat rooms though?
Add .of('/chat/room_name')?
Or store an array of everybody in a room?
Socket.IO v0.7 now gives you one Socket per namespace you define:
var room1 = io.connect('/room1');
room1.on('message', function () {
// chat socket messages
});
room1.on('disconnect', function () {
// chat disconnect event
});
var room2 = io.connect('/room2');
room2.on('message', function () {
// chat socket messages
});
room2.on('disconnect', function () {
// chat disconnect event
});
With different sockets, you can selectively send to the specific namespace you would like.
Socket.IO v0.7 also has concept of "room"
io.sockets.on('connection', function (socket) {
socket.join('a room');
socket.broadcast.to('a room').send('im here');
io.sockets.in('some other room').emit('hi');
});
Source: http://socket.io/#announcement
Update: Both Now.js and Bridge are now dead, see now.js dead and bridge dead. Socket.io seems to have adopted the callback feature as of v0.9, which is a good step forward.
While it isn't directly Socket.io related, Now.js (a higher level abstraction ontop of Socket.io) supports groups - http://nowjs.com/doc
They have a multi-room-chat example in their offocial repo here: https://github.com/Flotype/now/blob/master/examples/multiroomchat_example/multiroomchat_server.js

Resources