Fabrizio Fenoglio
Fabrizio Fenoglio

Reputation: 5947

Node + Redis + PHP getting RealTime Issue

I'm building a Pub/Sub Server in NodeJS + Redis + PHP, after dig in on the subject and starting building something I came across some confusion that I saw on the process.

Example Scenario:

Set up view:

<input type="text" id="message">
<button id="sendMessage">Send alert</button>

Client side Js

var socket = io.connect('http://127.0.0.1:3000/');

socket.on('notification', function (data) {

    alert(data.message);
    console.log('message received');
});

// Ajax event
$(document).on('click','#sendMessage', function(){

    $.ajax({
      url: '/send/notification',
      method: 'POST',
      type: 'JSON',
      data: { message: $('#message').val() },
      success:function(data)
      {
        console.log('message sent');
      }
    })
});

Php Server Side for Communicate to redis

$message = $_POST['message'];
$redis = new Predis\Client();
$redis->publish('notification',$message);

NodeJs

At this point on my NodeJs server I will go to listen for the message event in redis for then broadcast the event to sockets but here is the first issue I met.

var http = require('http');
var app  = http.createServer();
var io   = require('socket.io')(app);
var redis = require('redis');

app.listen(3000,'127.0.0.1',function(){
    console.log("listening:", app.address().port, app.address().address);
});

io.on('connection', function(client){
    var redisClient = redis.createClient();

    redisClient.subscribe('notification');

    redisClient.on("message",function(channel,data){

          // This line should broadcast to all the client except the sender
          socket.broadcast.emit(channel,JSON.parse(data));

          console.log(channel);
    });
});

Issue

At this point where I console.log() the channel I can see on my terminal 5 logs "notification" Why 5?

When socket.io broadcast the event, it does that 5 times, sending in this case 5 alert() to the clients and 4 to the sender instead of 1 for all clients a 0 for the sender.

The times are depending on how many users are connected I really don't understand what I missed, because it shouldn't be the right behaviour.

I already tried to put the creation of the redisClient and the subscription of a channel outside of the io.on('connection') with no luck, same result.


Another Test

if I broadcast the event on the connection of the socket like so:

io.on('connection', function(client)
{
     client.broadcast.emit('notification','hello');
});

It works correctly, so I think is redis problem. Any suggest will be really appreciated.

Upvotes: 0

Views: 1488

Answers (2)

Piyush
Piyush

Reputation: 1

I have done same in one project.Only difference is I publish notification from node.js but i did not get any issue.There is broadcast only open socket.

Upvotes: -1

Michael
Michael

Reputation: 1322

You are having 5 clients connected, so the "connection" event is fired 5 times. Thus there are 5 listeners for redis which all then broadcast.

There are two ways to do it correctly:

a) 1 listener per connection, only send the message to the connection

io.on('connection', function(socket){
    var redisClient = redis.createClient();

    redisClient.subscribe('notification');

    redisClient.on("message",function(channel,data){

          // This line should broadcast to only the current connection
          socket.emit(channel,JSON.parse(data));

          console.log(channel);
    });
});

b) 1 global listener, broadcast to all clients

var redisClient = redis.createClient();

redisClient.subscribe('notification');

redisClient.on("message",function(channel,data){

   // This line should broadcast to all the client except the sender
   io.sockets.emit(channel,JSON.parse(data));

   console.log(channel);
});

I would prefer method b) as it only needs one redis connection.

Upvotes: 2

Related Questions