我正在使用 nodejs、socket.io 和 redis 创建一个高级聊天系统。目标是建立一个聊天系统,其中一个联合聊天室供所有用户使用,但有额外的可能性来获得一对一支持的私人聊天。
我的 app.js 如下所示:
/********************************** Required **********************************/
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
var cluster = require('cluster')
, http = require('http')
, numCPUs = require('os').cpus().length;
/* this is for redis integration */
var redis = require('redis')
, RedisStore = require('socket.io/lib/stores/redis')
, pub = redis.createClient()
, sub = redis.createClient()
, client = redis.createClient();
io.set('store', new RedisStore({
redisPub: pub,
redisSub: sub,
redisClient: client
}));
/********************************** Required **********************************/
/********************************** Objects **********************************/
function User(socketID, username, role) {
var _socketID = socketID;
var _username = username;
var _role = role;
var _userObjectList = null;
console.log('user is being created');
this.getSocketID = function() {
return _socketID;
}
this.getUsername = function() {
return _username;
}
this.getRole = function() {
return _role;
}
this.setRole = function(role) {
_role = role;
_userObjectList = new Array();
}
this.addUser = function(userObject) {
_userObjectList[userObject.getUsername()] = userObject;
}
this.getUserCount = function() {
if(_role != Roles.staff)
return -1;
return Object.keys(_userObjectList).length;
}
}
/********************************** Objects **********************************/
/********************************** Static Variables **********************************/
function Roles() {
}
Roles.user = "user";
Roles.staff = "staff";
/********************************** Static Variables **********************************/
/********************************** Variables **********************************/
// usernames which are currently connected to the chat
var usernames = {};
var staff = {};
var socketIDList = {};
var users = new Array();
/********************************** Variables **********************************/
/********************************** Functions **********************************/
function getUsernames() {
return Object.keys(users);
}
function getStaffObjects() {
var objects = new Array();
console.log('getting users ' + Object.keys(users));
for (key in users) {
console.log('testing if ' + key + ' is a staff member');
if(users[key].getRole() == Roles.staff) {
console.log(key + ' is a staff member');
objects[key]=users[key];
}
}
return objects;
}
function isValidUsername(username) {
if(username == null)
return false;
var validation = /^[a-zA-Z]\w{2,20}/g;
return validation.test(username);
}
/********************************** Functions **********************************/
/********************************** Cluster **********************************/
if (cluster.isMaster) {
server.listen(8080);
console.log('listening now');
// Fork workers.
console.log('i am the master');
for (var i = 0; i < numCPUs; i++) {
console.log('forking cluster node ' + i);
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case its a HTTP server
console.log('i am worker #' + cluster.worker.id);
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}
/********************************** Cluster **********************************/
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.sockets.on('connection', function (socket) {
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
socket.on('pm', function(to, message) {
var id = users[to].getSocketID();
io.sockets.socket(id).emit('updatePrivateChat', socket.username, message);
});
socket.on('staffMessage', function(to, message) {
console.log("message to " + to + ": " + message);
var id = users[to].getSocketID();
io.sockets.socket(id).emit('updateStaffChat', socket.username, message);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
if(isValidUsername(username) && users[username] == null){ // user does not already exist
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
users[username] = new User(socket.id, socket.username, Roles.user);
console.log('added user ' + username + '. now we have ' + Object.keys(users).length + " users");
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', getUsernames());
}
else{
socket.emit('usernameExists', username);
}
});
// when the user wants to chat with staff
socket.on('contactStaff', function(){
/***************** search for a free staff ***************/
var member = null;
console.log('user ' + users[socket.username].getUsername() + ' wants to contact staff');
var staffList = getStaffObjects();
if(Object.keys(staffList).length == 0)
console.log('error. no staff available');
else {
var staffNumber = Math.floor(Math.random() * (Object.keys(staffList).length-1));
var i = 0;
console.log("StaffList Length: " + Object.keys(staffList).length);
for(key in staffList) {
if(i == staffNumber) {
member = staffList[key]
staffname = member.getUsername();
break;
}
i++;
}
console.log('adding ' + socket.username + ' to ' + member.getUsername());
member.addUser(users[socket.username]);
console.log('new contact for staff member. ' + member.getUsername() + ' has now ' + member.getUserCount() + ' contacts');
console.log('staffFound: ' + member.getUsername() + ", user: " + socket.username);
io.sockets.socket(users[socket.username].getSocketID()).emit('staffFound', member.getUsername());
io.sockets.socket(users[socket.username].getSocketID()).emit('updateStaffChat', member.getUsername(), "Sie haben einen Mitarbeiter kontaktiert. Was kann ich für Sie tun?");
io.sockets.socket(member.getSocketID()).emit('staffFound', socket.username);
io.sockets.socket(member.getSocketID()).emit('updateStaffChat', socket.username, "Sie wurden von dem Benutzer " + socket.username + " kontaktiert");
}
});
// register a staff member
socket.on('registerAsStaff', function(password){
if(password != "abc")
return false;
var staffname = socket.username;
users[socket.username].setRole(Roles.staff);
console.log('new staff member: ' + socket.username + " with role: " + users[socket.username].getRole());
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
users[socket.username] = null;
delete users[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
});
我的问题:聊天工作正常。集群本身正在工作,但我需要正确的 redis 集成。谁能帮助我如何做到这一点?我的解决方案通常是针对大量客户的高性能解决方案(第一步最多 1000-2000)吗?- 我知道我将不得不进行基准测试等等,但是你看到有什么东西会阻止我这样做吗?
我还需要一个解决方案,以便稍后拥有多个实例。我在http://blog.cloudfoundry.com/2013/01/24/scaling-real-time-apps-on-cloud-foundry-using-node-js-and-redis/上找到了该描述。我了解 socket.io 的多个实例的问题,但我不了解实现。有人可以提供一个简短的例子来说明如何做到这一点吗?