当有玩家连接过来,服务器需要做一些处理,这个很有必要。
如果你在app.configure(...()=>{
//增加connector的事件,结果是无效的。因为app还没有start。connector还没有初始化。
})
事实上是app.configure的回调是立马执行的。
下面是configure的代码实现:
Application.configure = function (env, type, fn) {
var args = [].slice.call(arguments);
fn = args.pop();
env = type = Constants.RESERVED.ALL;
if(args.length > 0) {
env = args[0];
}
if(args.length > 1) {
type = args[1];
}
if (env === Constants.RESERVED.ALL || contains(this.settings.env, env)) {
if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type)) {
fn.call(this);
}
}
return this;
};
//所以在configure回调处理是不行的。在这里主要处理服务器启动前,需要处理的配置。
后来发现,app会调用lifecycle.js,
会加载app/servers/xxxx/lifecycle.js,//xxxx是servers.json配置的服务器名称
并且对应了服务器启动的四个周期,分别是beforeStartup, afterStartup,beforeShutdown,afterStartall,因为我们需要在服务启动后,才能注册connection事件,所以我们应该在afterStartup中,添加connection事件处理。具体看下面的代码。
如app/servers/connector/lifecycle.js
let {SessionCheckOnConnect} = require("../../module/sessioncheck");
module.exports.beforeStartup = function(app, cb) {
// do some operations before application start up
//服务器启动前
//log.debug("beforeStartup:" + app.getServerId());
cb();
};
module.
exports.
afterStartup
=
function(
app,
cb) {
let connector
= app.components[
'__connector__'];
connector.connector.
on(
'connection',(
socket)
=>{
SessionCheckOnConnect(connector.session, socket); //这个是对连接过来处理的函数,主要用于绑定相关函数
});
//服务器启动后
//log.debug("afterStartup:" + app.getServerId());
cb();
};
module.
exports.
beforeShutdown
=
function(
app,
cb) {
//服务器关机前
//log.debug("beforeShutdown:" + app.getServerId());
cb();
};
module.
exports.
afterStartAll
=
function(
app) {
//所有服务器启动后
//log.debug("afterStartAll:" + app.getServerId());
};
//下面是sessionCheck的完整代码
let {utils, getLogger} = require("../../jscommon");
let log = getLogger(__filename);
const MIN_INTERVAL = 1000;
const DEFAULT_TIMEOUT = 30000;
/**
* session检查类, 用于检查没有登录的连接,如果超时指定的时间没有登录成功,则踢掉
*/
class SessionCheck {
constructor() {
//super();
this.m_app = null;
this.m_timeid = null;
this.m_open = false;
this.m_interval = MIN_INTERVAL;
this.m_timeout = DEFAULT_TIMEOUT;
this.m_sidmap = {};
}
/**
* 初始化会话检查
* @param {Application} paramApp pomelo的application对象
* @param {boolean} paramOpen 是否打开会话检查
* @param {number} paramInterval 每次检查的时间间隔,最小1000ms
* @param {number} paramTimeout 会话的超时时间
* @return {void}
*/
init(paramApp, paramOpen, paramInterval, paramTimeout) {
this.m_app = paramApp;
if(!Number.isInteger(paramInterval) || paramInterval < MIN_INTERVAL) {
this.m_interval = MIN_INTERVAL;
}
else {
this.m_interval = paramInterval;
}
if(!Number.isInteger(paramTimeout) || paramTimeout < 10){
this.m_timeout = DEFAULT_TIMEOUT;
}
else {
this.m_timeout = paramTimeout;
}
if(utils.isNotNull(this.m_timeid)) {
clearInterval(this.m_timeid);
this.m_timeid = null;
}
if(paramOpen) {
this.m_open = true;
}
else {
this.m_open = false;
}
if(this.isOpen) {
log.info("open SessionCheck: interval=" + this.m_interval);
this.m_timeid = setInterval(()=>{ this.__onTick(); }, this.m_interval);
}
}
/**
* 清空被检查的session id
* @return {void}
*/
clear() {
this.m_sidmap = {};
}
/**
* 当有连接过来时
* @param {Number} paramSessionId 连接过来的id
* @return {void}
*/
doConnect(paramSessionId) {
let v = {sid:paramSessionId, t:Date.now()};
this.m_sidmap[paramSessionId] = v;
}
/**
* 当前连接关闭时
* @param {Number} paramSessionId 被关闭的连接
* @return {void}
*/
doClose(paramSessionId, /*paramReason*/) {
let v = this.m_sidmap[paramSessionId];
if(utils.isNotNull(v)) {
delete this.m_sidmap[paramSessionId];
}
//this.emit("close", paramSessionId, paramReason);
}
/**
* 当有连接绑定时
* @param {Number} paramSessionId 被绑定的连接
* @return {void}
*/
doBind(paramSessionId, /*paramUID*/) {
//this.emit("bind", paramSessionId, paramUID);
let v = this.m_sidmap[paramSessionId];
if(utils.isNotNull(v)) {
delete this.m_sidmap[paramSessionId];
}
}
/**
* 每次检查tick到达
* @private 私有函数
*/
__onTick() {
let nNow = Date.now();
let app = this.app;
if(utils.isNull(app)||utils.isNull(app.sessionService)) {
log.error('app is null');
return;
}
let nTimeout = nNow - this.m_timeout;
let sidmap = this.m_sidmap;
for(let sid in sidmap) {
let v = sidmap[sid];
if(utils.isNull(v)) {
delete sidmap[sid];
continue;
}
if(v.t < nTimeout) {
log.debug('session:' + v.sid + ' is timeout');
delete sidmap[sid];
app.sessionService.kickBySessionId(sid);
}
log.debug(`sid=${sid} t = ${v.t - nTimeout}s`);
}
}
get app() {
return this.m_app;
}
get isOpen() {
return this.m_open;
}
}
/**
* 会话检查对象
*/
let sessionCheck = new SessionCheck();
/**
* 会话检查的连接函数
* @param {Sessions} paramSessionService pomelo的会话管理service
* @param {Socket} paramSocket 连接过来的socket对象
* @return {void}
*/
function SessionCheckOnConnect(paramSessionService, paramSocket){
let sid = paramSocket.id;
sessionCheck.doConnect(sid);
let session = paramSessionService.get(sid);
if(utils.isNotNull(session)) {
session.on('closed', (paramSession, paramReason)=>{
sessionCheck.doClose(paramSession.id, paramReason);
});
session.on('bind', (uid)=>{
sessionCheck.doBind(sid, uid);
});
}
else {
log.debug("not found session by sid:", sid);
}
}
exports.sessionCheck = sessionCheck;
exports.SessionCheckOnConnect = SessionCheckOnConnect;
//然后在app.js connector加入对它的初始化
app.configure('production|development', 'connector', async ()=> {
log.info("connector configure...: ", app.servers);
app.set('connectorConfig',
{
connector: pomelo.connectors.hybridconnector,
heartbeat: 3,
useDict: true,
useProtobuf: true,
handshake: function (msg, cb) {
let user = msg.user;
if (!user || !user.key || user.key !== app.game_config.static_config.common.key) {
cb(404, {});
return;
}
cb(null, {});
}
});
//这里加入初始化sessionCheck
let sessioncfg = static_config.common.session;
let {sessionCheck} = require('./app/module/sessioncheck');
sessionCheck.init(app, sessioncfg.open, sessioncfg.interval, sessioncfg.timeout);
});
在这个实现过中用,使用了connection,bind,closed等事件。早期的版本是用查询所有的session来判,现在这个版本,则只会对新来的connection做检查判断。这样就提升了效率。