Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event Broadcasting to a Specific User (socket.io, redis, node)

I want to make a notifications system based on events triggered by a user and gets delivered to another user ('X wrote on your wall). I understand the logic of broadcasting event, but if I broadcast, the message will be sent to everyone on the channel.

I am using Laravel 5 and I used Jeffrey Way's Nodejs, Redis, Socket.io tutorial. Very helpful.

For broadcasting and catching the messages,

  • Server.js

    var server = require('http').Server();
    var io = require('socket.io')(server);
    
    var Redis = require('ioredis');
    var redis = new Redis();
    
    redis.subscribe('globalNotificationChannel');
    
    redis.on('message', function(channel, message) {
        message = JSON.parse(message);
    
        io.emit(channel + ':' + message.event, message.data);
    });
    
    server.listen(3000);
    
    • Client

      <script>
         var socket = io('http://localhost:3000');
      
         socket.on('globalNotificationChannel:WroteOnWall', function(data) {
              //     (Notification Received)
             //      console.log(data.message)
           }.bind(this));
         }
      <script>
      
    • Triggering Action

      $data = [
        'event' => 'WroteOnWall',
        'data' => [
            'message' => $username + ' wrote on your wall.'
        ]
      ];
      
      Redis::publish('globalNotificationChannel', json_encode($data));
      

Until here everything is clear however at this point I got lost. I have couple of questions.

  • First, how do I deliver the triggered event to a specific user's channel if everyone gets subscribed on the same channel.

  • Or is there a way of making unique channels? If so, how can I store the unique_channel_ids to prevent any bug?

  • And secondly, I have seen people are using cluster. Is it a good use for my case? Will it give me any benefits to me?

  • Note that on the Client side, I inserted it in master page as I need to run in every page, but now, I need to call 'connection' and 'listening' in every page. When re-run the connection scripts won't I end up with differing socketids?

Edit: I found this SO answer, and I believe it solves my problem, but I couldn't adapt it on redis instance.

like image 395
senty Avatar asked Mar 14 '23 13:03

senty


1 Answers

Alright here it is as per #2 in my comment above. Forgive me for not including Redis into the mix here.

Your server probably has some sort of authentication login endpoint. I'm going to use json web token in this example. You may use something else. But you will need a way identify your users in your socket connections. https://auth0.com/blog/2014/01/15/auth-with-socket-io/

A client authenticates in some way perhaps a RESTful API call and gets a json web token:

//Server

var jwt = require('jsonwebtoken');
var returnedToken = jwt.sign(user, app.get('superSecret'), {
            expiresInMinutes: 1440 // expires in 24 hours
        });

Then they connect to your socket.io server passing that token in the query as ?token="fasfsadfsaf"

//Client

var socket = io('http://localhost:3000', { query: 'token='+token });

On your sever you handle decoding the token into a user

//Server

//This adds the jwt session token to handshake when connecting to the socket
var socketioJwt = require('socketio-jwt');
io.use(socketioJwt.authorize({
    secret:'superSecret',
    handshake:true
}));

//When a user connects throw them in their own unique room

io.on('connection', function (socket) {
    //This will get the encoded user object in the JWT token
    var user = socket.decoded_token;

    //This puts them into their unique room
    socket.join('user_room_'+user._id);
});

The user can now be reached by broadcasting to that room

io.to('user_room_'+referenceToSameUserId).emit('Your message');

For your other questions:

  • Cluster will help you scale across multiple processes/servers. Redis helps with this as well. Add this when you got the basic frame down.

  • If your users are reconnecting at every page perhaps the pages should be loaded with ajax? You don't have to worry about different socket ids if you are identifying the users by some sort of authentication method rather than by the socket id.

Sorry if this answer isn't a complete answer, I'm new to this stuff too as well. But hopefully this is some direction that you can work with. Just helping some more since I started off in the comments.

like image 92
Dave Thomas Avatar answered Mar 17 '23 02:03

Dave Thomas