I'm trying to make a push notification system, but to do that I need to find the proper way to emit something to ALL active sockets connections of a user (we suppose the logged-in User1 has opened my website in more than one tab of his web browser)
So, I was saving all active sockets-id in MySQL when a user was connected to his account (with sessions) :
user_id | socket_id
1 | 4lf3dsg2bx1u9d # User 1...
1 | 75ksdfzfff1u3d # ...has two active socket connections
2 | GEdsfoorbar1z4
and something like the following in my script :
io.sockets.sockets[socket_id].emit(...) # for each sockets of User1
But, I'm sure MySQL isn't the best way to sort sockets...
Does someone have a better solution ? Like a io.sockets.has('user=1') or something magic ?
I also tested the solution to stock every sockets by user-id in an Object but what about performance ?
Your strategy of storing socket IDs isn't horrible, although storing them using something like Redis would be better than MySQL. But for your case, you don't need to store the socket IDs at all: just use Socket.io's rooms instead. For example, every time User #1 establishes a socket connection, on the server do:
socket.join('user_1');
Then broadcast to everyone in the room (i.e. all the sockets that the user is connected with) using:
io.sockets.in('user_1').emit('event_name', data);
You can actually retrieve the IDs of all the sockets in a room, but the only time I really find it necessary is to count the number of IDs to see if the user is still connected.
Full docs for rooms are on the Socket.io wiki.
Related
I am working on setting up socket.io in cluster mode using PM2.
I am using socket.io-redis package and it works fine in cluster mode.
But the problem arises when I want to access all connected sockets. Because processes don't know about socket connections in other processes in cluster mode.
I thought socket.io-redis keeps track of all the connected sockets and all its session info but it didn't.
Is there any way or solution to access all the socket connection existing in all processes in socket.io/Nodejs?
Socket.io-redis does keep track in a sense..
From their docs
"The Redis adapter extends the broadcast function of the in-memory adapter: the packet is also published to a Redis channel (see below for the format of the channel name).
Each Socket.IO server receives this packet and broadcasts it to its own list of connected sockets."
So basically, redis is used as the broker to tell each socket server to emit based on X channel etc. Allowing you to have a socket.io server in cluster mode work, but as you have mentioned it can fall short when you need to keep track of things outside of just an emit.
So where does this leave us.. Well you can use custom hooks via socket.io-redis but personally I found it to be really difficult to understand and use and had limited success personally. I think with the new version of socket.io and socket.io redis there were some tweaks to make this simpler however I have not tried them.
Instead, what we do is use redis hset and jget to store the socket and an ID of a users, then when we want to get all users online we can query redis to get the list of online users or users in a specific room etc.
What you will want to do is add the redis package and connect in additon to the regular pub / sub.
Then, when a user joins a room or your server for that matter you will do an hset. On the first join ours looks something like this
redis.hset([collection-name],[Field],[value])
So in code it looks like
redis.hset(decoded.cID,"socket-" + socket.id,socket.nickname)
This will set a value in redis, so the collection name is a value ( for us its a unique id of the channel ) then we stock the 'socket.id' for the Field along with a 'nick-name' for the value. This value is the users ID OR its anonymous if they are not logged in
Then, when we want to grab who is in a room we use the hget command
redis.HGETALL([collection-name],function(err,results){}
So inside of say the emit, we call the redis.HGETALL command to get all items inside a specific collection that we pass in and send that back to all connected users.
I want to get no of connected users in socket.io and i am having multiple socket.io servers
For ex. when user1 connects to server1 and joins room room1
Below statements returns gives the connected Users list
then
io.nsps['/'].adapter.rooms["room1"]
or
io.sockets.adapter.rooms["room1"]
And also i tried many other solutions available on SO, and google.
But when i use above statements in server2 it does not return anything.
How can i get no of connected user in all server for specific room ?
I am using socket.io-redis module to communication between multiple processes.
If you want to share the sessions/rooms etc, you probably need to use redis.
Here there's the doc for using multiple nodes (which is what you have as far as I understand) and to use redis to pass events between one and the other nodes. http://socket.io/docs/using-multiple-nodes/#passing-events-between-nodes
And yes, to check the sessions in one room the following is correct io.nsps['/'].adapter.rooms["room1"].
Oops, just read now that you state about using socket.io-redis already. If it is configured properly it should work, at least it does for me.
In our app, every time a user signs in a new connection in socket.io is created. So if a user signs in simultaneously on more devices, they behave as separate connections. Instead of creating a new connection every time, I'd like to check whether the user is already connected to socket.io and if he is, I'd like to connect him to already established connection. How can this be done?
From the socket.io perspective, those ARE two connections. But if it was my app, I would do something like this:
add a user identifier (userid, username, something you identify users by) to both the users' socket.io connection (so, each time you send a message to the client, you also pass this id)
pass this id also to the client-side code on init.
So now the socket on the server side has its' socket.io id, but also your user id. Anyway, to proceed:
send this user-id to the client in it's javascript files.
on the client-side code, make a small adjustment to socket.io handler - for each received message (say, broadcast), you can now check if it's your current user-id and instead of saying 'User #351 says Hi' you can say 'You said Hi on another device' or something like that.
Of course, I'm not socket.io expert, there's could already exist a framework or lib addressing this.
I have a node.js tcp server that is used as a backend to an iPhone chat client. Since my implementation includes private group chats I store a list of users and what chat room they belong to in memory in order to route messages appropriately. This all works for fine assuming my chat server will always be on one machine, but when/if I need to scale horizontally I need a good way of broadcasting messages to clients that connect to different servers. I don't want to start doing inter-process communication between node servers and would prefer sharing state with redis.
I have a few ideas but I'm wondering if anyone has a good solution for this? To be clear here is an example:
User 1 connects to server 1 on room X, user 2 connects to server 2 on room X. User 1 sends a message, I need this to be passed to user 2, but since I am using an in memory data structure the servers don't share state. I want my node servers to remain as dumb as possible so I can just add/remove to the needs of my system.
Thanks :)
You could use a messaging layer (using something like pub/sub) that spans the processes:
Message Queue
-------------------------------------------------------------------------------
| |
ServerA ServerB
------- -------
Room 1: User1, User2 Room 1: User3, User5
Room 2: User4, User7, User11 Room 2: User6, User8
Room 3: User9, User13 Room 3: User10, User12, User14
Let's say User1 sends a chat message. ServerA sends a message on the message queue that says "User1 in Room 1 said something" (along with whatever they said). Each of your other server processes listens for such events, so, in this example, ServerB will see that it needs to distribute the message from User1 to all users in its own Room 1. You can scale to many processes in this way--each new process just needs to make sure they listen to appropriate messages on the queue.
Redis has pub/sub functionality that you may be able to use for this if you're already using Redis. Additionaly, there are other third-party tools for this kind of thing, like ZeroMQ; see also this question.
Redis is supposed to have built in cluster support in the near future, in the mean time you can use a consistent hashing algorithm to distribute your keys evenly across multiple servers. Someone out there has a hashing module for node.js, which was written specifically to implement consistent hashing for a redis cluster module for node.js. You might want to key off the 'room' name to ensure that all data points for a room wind up on the same host. With this type of setup all the logic for which server to use remains on the client, so your redis cluster can basically remain the same and you can easily add or remove hosts.
Update
I found the consistent hashing implementation for redis I was talking about, it gives the code of course, and also explains sharding in an easy to digest way.
http://ngchi.wordpress.com/2010/08/23/towards-auto-sharding-in-your-node-js-app/
I started looking into node and socket.io.
I already have created a simple chat application and I am amazed at how easy it was.
Now, I would like to take a little bit further and provide a list of online users that have the ability to chat with each other in private.
What would be the best way to approach this?
I read on 0.7's new room feature. Would that be a way to go? Dynamically create a new room each time 2 users need to chat in private? But how the second user is going to be notified of the new room created, so that he can connect there?
Is it better to handle all the above logic myself? Store the rooms and users server side and loop through them each time and send messages to the appropriate ones?
Thanks
If the only functionality you want is for two people to be able to send messages to one another (and not groups of people to have a room), then the logic could be something like this:
When a user connects, store their connection in an object keyed by their username (or in any other data structure that ensures you can find a specific user's connection).
When a Bob wants to talk to Jeff, send the server an event stating such.
The server looks up Jeff's Socket.IO connection in the object from step 1.
The server uses this connection to send Jeff (and only Jeff) the private message.
Hej Thomas
if theres only 2 users talking you dont need use publish att all just send that message from the client to the server and let the server locate the other client and send it down.