Reputation: 5947
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:
NodeJs
correctly. alert
to all the connected users except himSet 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);
});
});
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
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
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