作者:shihuaping0918@163.com,转载请注明作者
pomelo中有session/frontendSession/backendSession/sessionService。名字看起来都有点像,这一篇准备讲session和sessionService。session是对用户连接的一个抽象,它会调用sessionService。sessionService是session的具体实现底层。session只在frontend服务器中存在。如果还有印象的话,应该能记得gate负责接受外部连接,做frontend的负载均衡,将连接分派到不同的frontend上。connector再做路由处理,转到不同的backend服务器上,也就是逻辑服务器。
在分析session.js前要先分析sessionService.js,因为session实际是抽象了sessionService。从底向上更容易理解。
sessionService.js,先看一下Session的定义,Session中有一个id,uid,还有一个frontendId,还有一个socket。id就是sid,uid就是用户id,socket就是连接对应的socket。ftontendId就是frontend服务器id。sid和uid分别是从哪里来的呢?
/**
* Session maintains the relationship between client connection and user information.
* There is a session associated with each client connection. And it should bind to a
* user id after the client passes the identification.
*
* Session is created in frontend server and should not be accessed in handler.
* There is a proxy class called BackendSession in backend servers and FrontendSession
* in frontend servers.
*/
var Session = function(sid, frontendId, socket, service) {
EventEmitter.call(this);
this.id = sid; // r
this.frontendId = frontendId; // r
this.uid = null; // r 注意这里,uid初始是null
this.settings = {};
// private
this.__socket__ = socket;
this.__sessionService__ = service;
this.__state__ = ST_INITED;
};
sid实际上是socket创建时指定的一个id。sid = socket.id;
,它是在connector中生成的,对于sioconnector,有这样一段代码。
var curId = 1;
sio.on('connection', function (socket) {
var siosocket = new SioSocket(curId++, socket); //curId++就是sid
self.emit('connection', siosocket);
siosocket.on('closing', function(reason) {
siosocket.send({route: 'onKick', reason: reason});
});
});
所以这个sid是connector内部维护的,在服务生命周期内自增的一个ID。uid这这个时候是null,没有赋值的。
connector.js
session.on('bind', function(uid) {
logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);
// update connection statistics if necessary
if (self.connection) {
self.connection.addLoginedUser(uid, {
loginTime: Date.now(),
uid: uid,
address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
});
}
self.app.event.emit(events.BIND_SESSION, session);
});
connectorService.js
Session.prototype.bind = function(uid) {
this.uid = uid;
this.emit('bind', uid);
};
uid是在bind事件发生时出现的。这个bind被调用的时候,把uid给赋进去了。注意是sessionService里的Session.on('bind'...)。pomelo把这个session的名字复用了太多次。有点混乱。
为了避免概念上的混乱,下面总结一下,先说明一下session是指对客户端连接的一个抽象,它里面包含uid,sid,socket,以后出现session这单个单词,就是指连接的抽象。pomelo核心中有一个session component,这个session component是对sessionService的抽象。sessionService位于service下,实现session管理的具体工作。session的具体结构是在sessionService中出现的,叫Session。很绕啊,这就是名字取得不好的弊端。
下面再来看一下session component和sessionService之间是什么样的关系。
var SessionService = require('../common/service/sessionService');
module.exports = function(app, opts) {
var cmp = new Component(app, opts);
app.set('sessionService', cmp, true);
return cmp;
};
/**
* Session component. Manage sessions.
*
* @param {Object} app current application context
* @param {Object} opts attach parameters
*/
var Component = function(app, opts) {
opts = opts || {};
this.app = app;
this.service = new SessionService(opts); //创建sessionService
//这段话结尾有个(),代表函数调用
var getFun = function(m) {
return (function() {
return function() {
return self.service[m].apply(self.service, arguments); //arguments是随调用变化的
};
})();
};
// proxy the service methods except the lifecycle interfaces of component
var method, self = this;
for(var m in this.service) { //遍历sessionService的成员
if(m !== 'start' && m !== 'stop') { //如果不是start和stop方法
method = this.service[m]; //取出成员
if(typeof method === 'function') { //如果成员是函数
this[m] = getFun(m); //把函数加到自己模块里
}
}
}
};
Component.prototype.name = '__session__';
根据上面的分析,session component实际上是对sessionService做了一层代理。把sessionService的函数都加载到sesssion component里来了。这个代理的实现,下一篇文章会专门去讲它。