在看pomelo的session之前,我们先来看看pomelo的组件加载过程:
[javascript] view plain copy print ?
- if(app.isFrontend()) {
- app.load(pomelo.connection, app.get('connectionConfig'));
- app.load(pomelo.connector, app.get('connectorConfig'));
- app.load(pomelo.session, app.get('sessionConfig'));
- app.load(pomelo.protobuf, app.get('protobufConfig'));
- app.load(pomelo.scheduler, app.get('schedulerConfig'));
- }
- app.load(pomelo.localSession, app.get('localSessionConfig'));
- app.load(pomelo.channel, app.get('channelConfig'));
- app.load(pomelo.server, app.get('serverConfig'));
- if(app.get('globalChannelConfig')) {
- app.load(pomelo.globalChannel, app.get('globalChannelConfig'));
- }
if(app.isFrontend()) { //前端服务器 才需要载入的组件
app.load(pomelo.connection, app.get('connectionConfig')); //connection组件用于维护连接,比如说连接的状态
app.load(pomelo.connector, app.get('connectorConfig')); //用于从客户端接收socket,并为其分配session等
app.load(pomelo.session, app.get('sessionConfig')); //用于维护管理session,只有前端服务器才有session
app.load(pomelo.protobuf, app.get('protobufConfig'));
app.load(pomelo.scheduler, app.get('schedulerConfig'));
}
app.load(pomelo.localSession, app.get('localSessionConfig'));
app.load(pomelo.channel, app.get('channelConfig')); //channel的维护
app.load(pomelo.server, app.get('serverConfig')); //当前server的具体一些东西,例如一些用户定义的handler等
if(app.get('globalChannelConfig')) {
app.load(pomelo.globalChannel, app.get('globalChannelConfig'));
}
由上面的代码可以看出,只有frontend类型的服务器才会有session,其余的则只有localsession,这个也很好理解,因为也只有frontend类型的服务器才会接受用户的连接,要搞清楚session这部分,那么还需要从socket的接收开始说起了。。
在前面的文章中可以知道,pomelo中,frontend类型的服务器会加载connector组件,用于接收用户的连接,而且一般情况下都是采用的websocket的形式。。
那么我们就来看看这里websocket接收到socket执行的操作吧:
[javascript] view plain copy print ?
- this.wsocket.sockets.on('connection', function (socket) {
- var siosocket = new SioSocket(curId++, socket);
- self.emit('connection', siosocket);
- siosocket.on('closing', function(reason) {
- if(reason === 'kick') {
- siosocket.send({route: 'onKick'});
- }
- });
- });
this.wsocket.sockets.on('connection', function (socket) { //当有新的连接建立的时候
var siosocket = new SioSocket(curId++, socket);
self.emit('connection', siosocket); //本身出发connector事件,用于通知外面的connector
siosocket.on('closing', function(reason) {
if(reason === 'kick') {
siosocket.send({route: 'onKick'});
}
});
});
这里pomelo对接收到的socket又进行了一层的封装,自己定义了一个siosocket,其实也比较的简单,来看看它的构造过程:
[javascript] view plain copy print ?
- var Socket = function(id, socket) {
- EventEmitter.call(this);
- this.id = id;
- this.socket = socket;
- this.remoteAddress = {
- ip: socket.handshake.address.address,
- port: socket.handshake.address.port
- };
-
- var self = this;
-
- socket.on('disconnect', this.emit.bind(this, 'disconnect'));
-
- socket.on('error', this.emit.bind(this, 'error'));
-
- socket.on('message', function(msg) {
- self.emit('message', msg);
- });
-
- this.state = ST_INITED;
-
-
- };
var Socket = function(id, socket) {
EventEmitter.call(this);
this.id = id; //id号
this.socket = socket; //保存真正的socket
this.remoteAddress = {
ip: socket.handshake.address.address,
port: socket.handshake.address.port
};
var self = this;
socket.on('disconnect', this.emit.bind(this, 'disconnect'));
socket.on('error', this.emit.bind(this, 'error'));
socket.on('message', function(msg) { //当接受到数据之后
self.emit('message', msg);
});
this.state = ST_INITED;
// TODO: any other events?
};
说白了就是两个比较重要的属性:分配的id和真正的socket,然后设置了事件处理的函数。。。
在创建了自己定义的siosocket之后,会激发connection事件,来看看这个是怎么处理的吧:
[javascript] view plain copy print ?
- this.connector.on('connection', function(socket) {
- bindEvents(self, socket);
- });
this.connector.on('connection', function(socket) { //为connector绑定事件处理函数
bindEvents(self, socket);
}); //on connection end
也就是为当前的siosocket绑定一些事件的处理函数,而且在里面会具体的为当前的连接分配session。。。
那么我们来看看这个函数:
[javascript] view plain copy print ?
- var bindEvents = function(self, socket) {
- if(self.connection) {
- self.connection.increaseConnectionCount();
- }
-
-
- var session = getSession(self, socket);
- var closed = false;
-
- socket.on('disconnect', function() {
- if(closed) {
- return;
- }
- closed = true;
- if(self.connection) {
- self.connection.decreaseConnectionCount(session.uid);
- }
- });
-
- socket.on('error', function() {
- if(closed) {
- return;
- }
- closed = true;
- if(self.connection) {
- self.connection.decreaseConnectionCount(session.uid);
- }
- });
-
-
- socket.on('message', function(msg) {
- var dmsg = msg;
- if(self.decode) {
- dmsg = self.decode(msg);
- } else if(self.connector.decode) {
- dmsg = self.connector.decode(msg);
- }
- if(!dmsg) {
-
- return;
- }
-
- handleMessage(self, session, dmsg);
- });
- };
var bindEvents = function(self, socket) {
if(self.connection) {
self.connection.increaseConnectionCount(); //增加当前connection的连接数目
}
//create session for connection
var session = getSession(self, socket); //为当前的socket分配session
var closed = false;
socket.on('disconnect', function() { //表示连接已经断开
if(closed) {
return;
}
closed = true; //表示当前的connection已经关闭了
if(self.connection) {
self.connection.decreaseConnectionCount(session.uid);
}
});
socket.on('error', function() { //连接发生了错误
if(closed) {
return;
}
closed = true;
if(self.connection) {
self.connection.decreaseConnectionCount(session.uid);
}
});
// new message
socket.on('message', function(msg) {//表示接收到数据
var dmsg = msg;
if(self.decode) {
dmsg = self.decode(msg);
} else if(self.connector.decode) {
dmsg = self.connector.decode(msg);
}
if(!dmsg) {
// discard invalid message
return;
}
handleMessage(self, session, dmsg); //这里是调用server具体的handler来处理收到的这些数据
}); //on message end
};
这里看到了吧,首先还为当前的connection分配了一个session,然后再进行其余的处理。。具体都在干什么,其实前面的文章应该也比较的清楚了。。那么就来看看session是怎么弄出来的吧:
[javascript] view plain copy print ?
- var getSession = function(self, socket) {
- var app = self.app, sid = socket.id;
- var session = self.session.get(sid);
- if(session) {
- return session;
- }
-
- session = self.session.create(sid, app.getServerId(), socket);
-
-
- socket.on('disconnect', session.closed.bind(session));
- socket.on('error', session.closed.bind(session));
- session.on('closed', onSessionClose.bind(null, app));
- session.on('bind', function(uid) {
-
- if(self.connection) {
- self.connection.addLoginedUser(uid, {
- loginTime: Date.now(),
- uid: uid,
- address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
- });
- }
- });
-
- return session;
- };
var getSession = function(self, socket) {
var app = self.app, sid = socket.id;
var session = self.session.get(sid); //直接从key-value里面取,如果没有的话,那么再创建
if(session) {
return session;
}
//用于创建session的参数有当前的socket的id,还有server的id,还有socket
session = self.session.create(sid, app.getServerId(), socket);
// bind events for session
socket.on('disconnect', session.closed.bind(session));
socket.on('error', session.closed.bind(session));
session.on('closed', onSessionClose.bind(null, app));
session.on('bind', function(uid) { //当有user绑定到当前的session上面来
// update connection statistics if necessary
if(self.connection) {
self.connection.addLoginedUser(uid, {
loginTime: Date.now(),
uid: uid,
address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
});
}
});
return session;
};
这里其实调用的是sessionservice的create来创建的session,而且传进去的参数也还算是比较多的。。有当前socket的id,server的id还有socket,那么我们来看看这个create函数做了什么事情吧:
[javascript] view plain copy print ?
-
- essionService.prototype.create = function(sid, frontendId, socket) {
- var session = new Session(sid, frontendId, socket, this);
- this.sessions[session.id] = session;
-
- return session;
- ;
//构建新的session,这里的sid就是socket的id,frontendId是服务器的id,socket就是当前的siosocket
SessionService.prototype.create = function(sid, frontendId, socket) {
var session = new Session(sid, frontendId, socket, this); //创建session
this.sessions[session.id] = session; //用socket的id来索引这个session
return session;
};
直接调用session的构造函数来创建session,而且还用session的id来索引这个session,其实这里这个id也就是socket的id。。我们来看看这个session的构造函数吧:
[javascript] view plain copy print ?
-
- var Session = function(sid, frontendId, socket, service) {
- EventEmitter.call(this);
- this.id = sid;
- this.frontendId = frontendId;
- this.uid = null;
- this.settings = {};
-
-
- this.__socket__ = socket;
- this.__sessionService__ = service;
- this.__state__ = ST_INITED;
- };
-
- util.inherits(Session, EventEmitter);
//session的构造函数
var Session = function(sid, frontendId, socket, service) {
EventEmitter.call(this);
this.id = sid; // session的id,其实就是socket的id
this.frontendId = frontendId; // 服务器id
this.uid = null; // r
this.settings = {}; //用于保存set的属性
// private
this.__socket__ = socket;
this.__sessionService__ = service; //一般情况下会这是为null
this.__state__ = ST_INITED;
};
util.inherits(Session, EventEmitter);
好了,那么到现在为止,整个session的创建过程就算弄完了。。
接下来我们在来看看整个sessionService所提供的一些服务吧,首先来看sessionService的构造:
[javascript] view plain copy print ?
- var SessionService = function(opts) {
- opts = opts || {};
- this.sessions = {};
- this.uidMap = {};
- };
var SessionService = function(opts) {
opts = opts || {};
this.sessions = {}; // sid -> session
this.uidMap = {}; // uid -> sessions //用于将uid与session绑定起来
};
这里sessions用于保存当前服务器所有的session,其中key是session的id,value则是对应的session
uidMap则是用于组织一个uid对应的所有的session,因为一个uid可能拥有多个session,这个也是需要组织起来的。。
然后来看几个比较重要的方法,首先是bind方法,用于将一个session与一个uid绑定起来:
[javascript] view plain copy print ?
-
- SessionService.prototype.bind = function(sid, uid, cb) {
- var session = this.sessions[sid];
-
- if(!session) {
- process.nextTick(function() {
- cb(new Error('session not exist, sid: ' + sid));
- });
- return;
- }
-
- if(session.uid) {
- if(session.uid === uid) {
-
- cb();
- return;
- }
-
-
- process.nextTick(function() {
- cb(new Error('session has already bind with ' + session.uid));
- });
- return;
- }
-
- var sessions = this.uidMap[uid];
- if(!sessions) {
- sessions = this.uidMap[uid] = [];
- }
-
- for(var i=0, l=sessions.length; i<l; i++) {
-
- if(sessions[i].id === session.id) {
- process.nextTick(cb);
- return;
- }
- }
-
- sessions.push(session);
-
- session.bind(uid);
-
- if(cb) {
- process.nextTick(cb);
- }
- };
//sid所属的session与uid放顶起来
SessionService.prototype.bind = function(sid, uid, cb) {
var session = this.sessions[sid]; //获取相应的session
if(!session) {
process.nextTick(function() {
cb(new Error('session not exist, sid: ' + sid));
});
return;
}
if(session.uid) {
if(session.uid === uid) {
// already binded with the same uid
cb();
return;
}
// already binded with other uid
process.nextTick(function() {
cb(new Error('session has already bind with ' + session.uid));
});
return;
}
var sessions = this.uidMap[uid]; //当前uid所有的session
if(!sessions) {
sessions = this.uidMap[uid] = [];
}
for(var i=0, l=sessions.length; i<l; i++) { //判断当前uid所有的session是否有当前这个session
// session has binded with the uid
if(sessions[i].id === session.id) {
process.nextTick(cb);
return;
}
}
//表示这个uid有了一个新的session,那么需要将它加入到uid的session队列当中
sessions.push(session);
//将这个session与uid帮顶起来
session.bind(uid);
if(cb) {
process.nextTick(cb);
}
};
代码很简单,一看就能明白,就是多了个uidMaps的处理。。。
接下来是比较重要的发送数据的方法:
[javascript] view plain copy print ?
-
- SessionService.prototype.sendMessage = function(sid, msg) {
- var session = this.sessions[sid];
-
- if(!session) {
- logger.debug('fail to send message for session not exits');
- return false;
- }
-
- return send(this, session, msg);
- };
//通过这个session来发送数据
SessionService.prototype.sendMessage = function(sid, msg) {
var session = this.sessions[sid];
if(!session) {
logger.debug('fail to send message for session not exits');
return false;
}
return send(this, session, msg);
};
其实还是很简答的,通过sid来找到相应的session,然后通过这个session来将msg发送出去。。其实最终还是调用的相应的socket来发送的数据。。。
然后就还有一个:
[javascript] view plain copy print ?
-
- essionService.prototype.sendMessageByUid = function(uid, msg) {
- var sessions = this.uidMap[uid];
-
- if(!sessions) {
- logger.debug('fail to send message by uid for session not exist. uid: %j',
- uid);
- return false;
- }
-
- for(var i=0, l=sessions.length; i<l; i++) {
- send(this, sessions[i], msg);
- }
- ;
//为当前这个uid的所有session广播数据
SessionService.prototype.sendMessageByUid = function(uid, msg) {
var sessions = this.uidMap[uid];
if(!sessions) {
logger.debug('fail to send message by uid for session not exist. uid: %j',
uid);
return false;
}
for(var i=0, l=sessions.length; i<l; i++) {
send(this, sessions[i], msg);
}
};
就是当前uid拥有的所有的session都要发送这个数据。。。。。
pomelo为每一个连接都分配了session,而且还要处理session与uid之间的关系。。。
还在session中封装了一些发送数据的方法