1

我正在使用 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 的多个实例的问题,但我不了解实现。有人可以提供一个简短的例子来说明如何做到这一点吗?

4

0 回答 0