various fixes for socket.io cluster
display user presence correctly
v1.18.x
barisusakli 10 years ago
parent 1a58ea6520
commit 77e956861a

@ -38,7 +38,7 @@
li { li {
float: left; float: left;
width: 48%; width: 100%;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -90,7 +90,7 @@
border-color: #46BFBD; border-color: #46BFBD;
background-color: #5AD3D1; background-color: #5AD3D1;
} }
&.on-homepage { &.on-categories {
border-color: #F7464A; border-color: #F7464A;
background-color: #FF5A5E; background-color: #FF5A5E;
} }

@ -141,10 +141,10 @@ define('admin/general/dashboard', ['semver'], function(semver) {
'<div>Connections</div>' + '<div>Connections</div>' +
'</div>'; '</div>';
var idle = data.socketCount - (data.users.home + data.users.topics + data.users.category); var idle = data.socketCount - (data.users.categories + data.users.topics + data.users.category);
updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount); updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount);
updatePresenceGraph(data.users.home, data.users.topics, data.users.category, idle); updatePresenceGraph(data.users.categories, data.users.topics, data.users.category, idle);
updateTopicsGraph(data.topics); updateTopicsGraph(data.topics);
$('#active-users').html(html); $('#active-users').html(html);
@ -266,7 +266,7 @@ define('admin/general/dashboard', ['semver'], function(semver) {
value: 1, value: 1,
color:"#F7464A", color:"#F7464A",
highlight: "#FF5A5E", highlight: "#FF5A5E",
label: "On homepage" label: "On categories list"
}, },
{ {
value: 1, value: 1,
@ -345,8 +345,8 @@ define('admin/general/dashboard', ['semver'], function(semver) {
graphs.registered.update(); graphs.registered.update();
} }
function updatePresenceGraph(homepage, posts, topics, idle) { function updatePresenceGraph(categories, posts, topics, idle) {
graphs.presence.segments[0].value = homepage; graphs.presence.segments[0].value = categories;
graphs.presence.segments[1].value = posts; graphs.presence.segments[1].value = posts;
graphs.presence.segments[2].value = topics; graphs.presence.segments[2].value = topics;
graphs.presence.segments[3].value = idle; graphs.presence.segments[3].value = idle;
@ -424,6 +424,9 @@ define('admin/general/dashboard', ['semver'], function(semver) {
function buildTopicsLegend() { function buildTopicsLegend() {
var legend = $('#topics-legend').html(''); var legend = $('#topics-legend').html('');
segments.sort(function(a, b) {
return b.value - a.value;
});
for (var i = 0, ii = segments.length; i < ii; i++) { for (var i = 0, ii = segments.length; i < ii; i++) {
var topic = segments[i], var topic = segments[i],
label = topic.tid === '0' ? topic.label : '<a title="' + topic.label + '"href="' + RELATIVE_PATH + '/topic/' + topic.tid + '" target="_blank"> ' + topic.label + '</a>'; label = topic.tid === '0' ? topic.label : '<a title="' + topic.label + '"href="' + RELATIVE_PATH + '/topic/' + topic.tid + '" target="_blank"> ' + topic.label + '</a>';

@ -105,8 +105,8 @@ app.cacheBuster = null;
case 'admin': case 'admin':
room = 'admin'; room = 'admin';
break; break;
case 'home': case 'categories':
room = 'home'; room = 'categories';
break; break;
} }
app.currentRoom = ''; app.currentRoom = '';

@ -12,6 +12,7 @@ var SocketIO = require('socket.io'),
user = require('../user'), user = require('../user'),
logger = require('../logger'), logger = require('../logger'),
ratelimit = require('../middleware/ratelimit'), ratelimit = require('../middleware/ratelimit'),
rooms = require('./rooms'),
Sockets = {}, Sockets = {},
Namespaces = {}; Namespaces = {};
@ -63,8 +64,8 @@ function onConnection(socket) {
function onConnect(socket) { function onConnect(socket) {
if (socket.uid) { if (socket.uid) {
socket.join('uid_' + socket.uid); rooms.enter(socket, 'uid_' + socket.uid);
socket.join('online_users'); rooms.enter(socket, 'online_users');
user.getUserFields(socket.uid, ['status'], function(err, userData) { user.getUserFields(socket.uid, ['status'], function(err, userData) {
if (err || !userData) { if (err || !userData) {
@ -77,7 +78,7 @@ function onConnect(socket) {
} }
}); });
} else { } else {
socket.join('online_guests'); rooms.enter(socket, 'online_guests');
socket.emit('event:connect'); socket.emit('event:connect');
} }
} }
@ -85,7 +86,7 @@ function onConnect(socket) {
function onDisconnect(socket, data) { function onDisconnect(socket, data) {
if (socket.uid) { if (socket.uid) {
var socketCount = Sockets.getUserSocketCount(socket.uid); var socketCount = Sockets.getUserSocketCount(socket.uid);
if (socketCount <= 0) { if (socketCount <= 1) {
socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'}); socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'});
} }
@ -96,6 +97,7 @@ function onDisconnect(socket, data) {
} }
}); });
} }
rooms.leaveAll(socket, data.rooms);
} }
function onMessage(socket, payload) { function onMessage(socket, payload) {
@ -218,27 +220,25 @@ Sockets.in = function(room) {
}; };
Sockets.getSocketCount = function() { Sockets.getSocketCount = function() {
// TODO: io.sockets.adapter.sids is local to this worker return rooms.socketCount();
// use redis-adapter
var clients = Object.keys(io.sockets.adapter.sids || {});
return Array.isArray(clients) ? clients.length : 0;
}; };
Sockets.getUserSocketCount = function(uid) { Sockets.getUserSocketCount = function(uid) {
// TODO: io.sockets.adapter.rooms is local to this worker return rooms.clients('uid_' + uid).length;
// use .clients('uid_' + uid, fn) };
var roomClients = Object.keys(io.sockets.adapter.rooms['uid_' + uid] || {}); Sockets.getOnlineUserCount = function() {
return Array.isArray(roomClients) ? roomClients.length : 0; var count = 0;
Object.keys(rooms.roomClients()).forEach(function(roomName) {
if (roomName.startsWith('uid_')) {
++ count;
}
});
return count;
}; };
Sockets.getOnlineAnonCount = function () { Sockets.getOnlineAnonCount = function () {
// TODO: io.sockets.adapter.rooms is local to this worker return rooms.clients('online_guests').length;
// use .clients()
var guestSocketIds = Object.keys(io.sockets.adapter.rooms.online_guests || {});
return Array.isArray(guestSocketIds) ? guestSocketIds.length : 0;
}; };
Sockets.reqFromSocket = function(socket) { Sockets.reqFromSocket = function(socket) {
@ -258,9 +258,7 @@ Sockets.reqFromSocket = function(socket) {
}; };
Sockets.isUserOnline = function(uid) { Sockets.isUserOnline = function(uid) {
// TODO: io.sockets.adapter.rooms is local to this worker return !!rooms.clients('uid_' + uid).length;
// use .clients('uid_' + uid, fn)
return io ? !!io.sockets.adapter.rooms['uid_' + uid] : false;
}; };
Sockets.isUsersOnline = function(uids, callback) { Sockets.isUsersOnline = function(uids, callback) {
@ -301,26 +299,29 @@ Sockets.getUsersInRoom = function (uid, roomName, callback) {
Sockets.getUidsInRoom = function(roomName, callback) { Sockets.getUidsInRoom = function(roomName, callback) {
callback = callback || function() {}; callback = callback || function() {};
// TODO : doesnt work in cluster
var uids = []; var uids = [];
var socketids = Object.keys(io.sockets.adapter.rooms[roomName] || {}); var socketids = rooms.clients(roomName);
if (!Array.isArray(socketids) || !socketids.length) { if (!Array.isArray(socketids) || !socketids.length) {
callback(null, []); callback(null, []);
return []; return [];
} }
for(var i=0; i<socketids.length; ++i) { for(var i=0; i<socketids.length; ++i) {
var socketRooms = Object.keys(io.sockets.adapter.sids[socketids[i]]); var socketRooms = rooms.clientRooms(socketids[i]);
if (Array.isArray(socketRooms)) { if (Array.isArray(socketRooms)) {
socketRooms.forEach(function(roomName) { socketRooms.forEach(function(roomName) {
if (roomName.indexOf('uid_') === 0 ) { if (roomName.startsWith('uid_')) {
uids.push(roomName.split('_')[1]); var uid = roomName.split('_')[1];
if (uids.indexOf(uid) === -1) {
uids.push(uid);
}
} }
}); });
} }
} }
callback(null, uids); callback(null, uids);
return uids; return uids;
}; };

@ -12,6 +12,7 @@ var nconf = require('nconf'),
logger = require('../logger'), logger = require('../logger'),
plugins = require('../plugins'), plugins = require('../plugins'),
emitter = require('../emitter'), emitter = require('../emitter'),
rooms = require('./rooms'),
websockets = require('./'), websockets = require('./'),
@ -58,7 +59,7 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
} }
if (socket.currentRoom) { if (socket.currentRoom) {
socket.leave(socket.currentRoom); rooms.leave(socket, socket.currentRoom);
if (socket.currentRoom.indexOf('topic') !== -1) { if (socket.currentRoom.indexOf('topic') !== -1) {
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid); websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
} }
@ -66,7 +67,7 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
} }
if (data.enter) { if (data.enter) {
socket.join(data.enter); rooms.enter(socket, data.enter);
socket.currentRoom = data.enter; socket.currentRoom = data.enter;
if (data.enter.indexOf('topic') !== -1) { if (data.enter.indexOf('topic') !== -1) {
data.uid = socket.uid; data.uid = socket.uid;
@ -80,19 +81,13 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
}; };
SocketMeta.rooms.getAll = function(socket, data, callback) { SocketMeta.rooms.getAll = function(socket, data, callback) {
var now = Date.now(); var roomClients = rooms.roomClients();
db.sortedSetCount('users:online', now - 300000, now, function(err, onlineRegisteredCount) {
if (err) {
return callback(err);
}
var rooms = {}; // TODO: websockets.server.sockets.manager.rooms; doesnt work in socket.io 1.x
var socketData = { var socketData = {
onlineGuestCount: websockets.getOnlineAnonCount(), onlineGuestCount: websockets.getOnlineAnonCount(),
onlineRegisteredCount: onlineRegisteredCount, onlineRegisteredCount: websockets.getOnlineUserCount(),
socketCount: websockets.getSocketCount(), socketCount: websockets.getSocketCount(),
users: { users: {
home: rooms['/home'] ? rooms['/home'].length : 0, categories: roomClients.categories ? roomClients.categories.length : 0,
topics: 0, topics: 0,
category: 0 category: 0
}, },
@ -103,10 +98,11 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
topTenTopics = [], topTenTopics = [],
tid; tid;
for (var room in rooms) { for (var room in roomClients) {
if (rooms.hasOwnProperty(room)) { if (roomClients.hasOwnProperty(room)) {
if (tid = room.match(/^\/topic_(\d+)/)) { tid = room.match(/^topic_(\d+)/);
var length = rooms[room].length; if (tid) {
var length = roomClients[room].length;
socketData.users.topics += length; socketData.users.topics += length;
if (scores[length]) { if (scores[length]) {
@ -114,8 +110,8 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
} else { } else {
scores[length] = [tid[1]]; scores[length] = [tid[1]];
} }
} else if (room.match(/^\/category/)) { } else if (room.match(/^category/)) {
socketData.users.category += rooms[room].length; socketData.users.category += roomClients[room].length;
} }
} }
} }
@ -135,14 +131,14 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
} }
topTenTopics.forEach(function(tid, id) { topTenTopics.forEach(function(tid, id) {
socketData.topics[tid] = { socketData.topics[tid] = {
value: rooms['/topic_' + tid].length, value: Array.isArray(roomClients['topic_' + tid]) ? roomClients['topic_' + tid].length : 0,
title: validator.escape(titles[id].title) title: validator.escape(titles[id].title)
}; };
}); });
callback(null, socketData); callback(null, socketData);
}); });
});
}; };
/* Exports */ /* Exports */

@ -0,0 +1,95 @@
'use strict';
// Temp solution until
// https://github.com/NodeBB/NodeBB/issues/2486
// and
// https://github.com/Automattic/socket.io/issues/1945
// are closed.
// Once they are closed switch to .clients() and async calls
var pubsub = require('../pubsub');
var rooms = {};
var clientRooms = {};
var roomClients = {};
rooms.enter = function(socket, room) {
socket.join(room);
pubsub.publish('socket:join', {id: socket.id, room: room});
};
rooms.leave = function(socket, room) {
socket.leave(room);
pubsub.publish('socket:leave', {id: socket.id, room: room});
};
rooms.leaveAll = function(socket, roomsToLeave) {
roomsToLeave.forEach(function(room) {
rooms.leave(socket, room);
});
};
pubsub.on('socket:join', onSocketJoin);
pubsub.on('socket:leave', onSocketLeave);
function onSocketJoin(data) {
clientRooms[data.id] = clientRooms[data.id] || [];
if (clientRooms[data.id].indexOf(data.room) === -1) {
clientRooms[data.id].push(data.room);
}
roomClients[data.room] = roomClients[data.room] || [];
if (roomClients[data.room].indexOf(data.id) === -1) {
roomClients[data.room].push(data.id);
}
}
function onSocketLeave(data) {
var index;
if (Array.isArray(clientRooms[data.id])) {
index = clientRooms[data.id].indexOf(data.room);
if (index !== -1) {
clientRooms[data.id].splice(index, 1);
if (!clientRooms[data.id].length) {
delete clientRooms[data.id];
}
}
}
if (Array.isArray(roomClients[data.room])) {
index = roomClients[data.room].indexOf(data.id);
if (index !== -1) {
roomClients[data.room].splice(index, 1);
if (!roomClients[data.room].length) {
delete roomClients[data.room];
}
}
}
}
rooms.clients = function(room) {
return Array.isArray(roomClients[room]) ? roomClients[room] : [];
};
rooms.clientRooms = function(id) {
return Array.isArray(clientRooms[id]) ? clientRooms[id] : [];
};
rooms.socketCount = function() {
return Object.keys(clientRooms || {}).length;
};
rooms.roomClients = function() {
return roomClients;
};
module.exports = rooms;

@ -1,6 +1,5 @@
<div class="row dashboard"> <div class="row dashboard">
<!-- Override for now, until the right sidebar graphs are fixed (pending socket.io resolution) --> <div class="col-lg-9">
<div class="col-lg-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">Forum Traffic</div> <div class="panel-heading">Forum Traffic</div>
<div class="panel-body"> <div class="panel-body">
@ -88,8 +87,8 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Override for now, until the right sidebar graphs are fixed (pending socket.io resolution) -->
<div class="col-lg-3 hide"> <div class="col-lg-3">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">Anonymous vs Registered Users</div> <div class="panel-heading">Anonymous vs Registered Users</div>
<div class="panel-body"> <div class="panel-body">
@ -108,7 +107,7 @@
<div class="panel-body"> <div class="panel-body">
<div class="graph-container pie-chart legend-up"> <div class="graph-container pie-chart legend-up">
<ul class="graph-legend"> <ul class="graph-legend">
<li><div class="on-homepage"></div><span>On Homepage</span></li> <li><div class="on-categories"></div><span>On categories list</span></li>
<li><div class="reading-posts"></div><span>Reading posts</span></li> <li><div class="reading-posts"></div><span>Reading posts</span></li>
<li><div class="browsing-topics"></div><span>Browsing topics</span></li> <li><div class="browsing-topics"></div><span>Browsing topics</span></li>
<li><div class="idle"></div><span>Idle</span></li> <li><div class="idle"></div><span>Idle</span></li>

Loading…
Cancel
Save