接着(一)所分析的代码
// app configure
app.configure('production|development', function() {
// route configures
app.route('chat', routeUtil.chat);
// filter configures
app.filter(pomelo.timeout());
});
根据开发环境是产品还是开发,调整服务器所使用的中间件,这里使用了路由器和过滤器的中间件,让我们进入函数来分析一下这些中间件到底做什么。
/**
* Set the route function for the specified server type.
*
* Examples:
*
* app.route('area', routeFunc);
*
* var routeFunc = function(session, msg, app, cb) {
* // all request to area would be route to the first area server
* var areas = app.getServersByType('area');
* cb(null, areas[0].id);
* };
*
* @param {String} serverType server type string
* @param {Function} routeFunc route function. routeFunc(session, msg, app, cb)
* @return {Object} current application instance for chain invoking
*
* @memberOf Application
*/
Application.route = function(serverType, routeFunc) {
var routes = this.get('__routes__');
if(!routes) {
routes = {};
this.set('__routes__', routes);
}
routes[serverType] = routeFunc;
return this;
};
route函数作用就是将 serverType 对应的功能函数写入routes的配置下,routefunc属于路由器的回调函数,route主要是记录路由器绑定的函数,接下来我们看看被绑定的函数routeUtil.chat
var exp = module.exports;
var dispatcher = require('./dispatcher');
exp.chat = function(session, msg, app, cb) {
var chatServers = app.getServersByType('chat');
if(!chatServers || chatServers.length === 0) {
cb(new Error('can not find chat servers.'));
return;
}
var res = dispatcher.dispatch(session.get('rid'), chatServers); //随机获取一个提供服务的服务器ID
cb(null, res.id);
};
函数主要的功能在于 dispatcher.dispatch(session.get('rid'), chatServers); 这个函数主要的功能是随机获取一个提供服务的服务器ID,并将di返回给上层的函数
var crc = require('crc');
module.exports.dispatch = function(uid, connectors) {
var index = Math.abs(crc.crc32(uid)) % connectors.length;
return connectors[index];
};
函数通过crc库和rid来随机生成一个数字,通过数字来随机抽取一个服务器为这个url请求作出相应,路由器的功能分析完毕
过滤器
/**
* add a filter to before and after filter
*
* @param {Object} filter provide before and after filter method. A filter should have two methods: before and after
*
* @memberOf Application
*/
Application.filter = function (filter) {
this.before(filter);
this.after(filter);
return this;
};
过滤器要求filter对象至少包含两个方法,过滤前和过滤后。
/**
* Add before filter.
*
* @param {Object|Function} bf before fileter, bf(msg, session, next)
*
* @memberOf Application
*/
Application.before = function (bf) {
var befores = this.get('__befores__');
if(!befores) {
befores = [];
this.set('__befores__', befores);
}
befores.push(bf);
return this;
};
before函数主要作用把filter存放到 before内存里面,为以后调用做准备,after同理。
可以看出来,flter函数需要一个叫filter的对象,对象需要至少包含after和before两种方法。我们看一下我们的主函数到底传的是什么值
// filter configures
app.filter(pomelo.timeout());
pomelo.timeout() ,在webstrom的IDE环境里面,显示该函数并没有定义,而实际上,该函数已经定义了,它是利用pomelo被声明的时候,动态加载的,接下来我们重新看看pomelo这个模块函数是怎么定义的,方便我们找出pomelo.timeout这个函数方法。
/*!
* Pomelo
* Copyright(c) 2012 xiechengchao <xiecc@163.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var fs = require('fs');
var path = require('path');
var application = require('./application');
/**
* Expose `createApplication()`.
*
* @module
*/
var Pomelo = module.exports = {};
/**
* Framework version.
*/
Pomelo.version = '0.1';
/**
* auto loaded components
*/
Pomelo.components = {};
/**
* auto loaded filters
* @type {Object}
*/
Pomelo.filters = {};
var self = this;
/**
* Create an pomelo application.
*
* @return {Application}
* @memberOf Pomelo
* @api public
*/
Pomelo.createApp = function (opts) {
var app = application;
app.init(opts);
self.app = app;
return app;
};
/**
* Get application
*/
Object.defineProperty(Pomelo, 'app', {
get:function () {
return self.app;
}
});
Pomelo.channelService = require('./common/service/channelService');
Pomelo.taskManager = require('./common/service/taskManager');
/**
* Auto-load bundled components with getters.
*/
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
if (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
function load() {
return require('./components/' + name); //寻找components下的js文件,并通过require加载到源码中
}
Pomelo.components.__defineGetter__(name, load);
Pomelo.__defineGetter__(name, load);
});
fs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) {
if (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
function load() {
return require('./filters/handler/' + name); //寻找 fileter下的js文件,并通过require加载到源码中
}
Pomelo.filters.__defineGetter__(name, load);
Pomelo.__defineGetter__(name, load);
});
源码当中,有几个函数是比较陌生的,需要拿出来分析一下
/**
* Get application
*/
Object.defineProperty(Pomelo, 'app', {
get:function () {
return self.app;
}
});
详细api介绍:http://www.cnblogs.com/rubylouvre/archive/2010/09/19/1831128.html
这里的调用目的,主要是当我们访问 Pomelo.app时,将会调用get函数,返回self.app。
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
if (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
function load() {
return require('./components/' + name); //寻找components下的js文件,并通过require加载到源码中
}
Pomelo.components.__defineGetter__(name, load);
Pomelo.__defineGetter__(name, load);
});
forEach遍历对应目录下js文件,并通过require加载到pomelo下
Pomelo.components.__defineGetter__(name, load);
Pomelo.__defineGetter__(name, load);
为了理解上面函数的意思,百度了一下相关的例子
Date.prototype.__defineGetter__('year', function() {return this.getFullYear();});
Date.prototype.__defineSetter__('year', function(y) {this.setFullYear(y)});
var now = new Date;
alert(now.year);
now.year = 2006;
alert(now);
相当于重载了 get的方法,将对象后面的属性作为参数传入到回调函数里面
在Pomelo下相当于调用timeout下的模块,意思如下:
Pomelo.timeout = function load() {
return require('./filters/handler/' + name);
}
当声明Pomelo.timeout时候,相当于调用?filters/handler/下的 timout.js文件
pomelo/lib/filters/handler/timeout.js
/**
* Filter for timeout.
* Print a warn information when request timeout.
*/
var logger = require('pomelo-logger').getLogger(__filename);
var DEFAULT_TIMEOUT = 3000;
module.exports = function(timeout) {
return new Filter(timeout || DEFAULT_TIMEOUT);
};
var Filter = function(timeout) {
this.timeout = timeout; //过期时间
this.timeouts = {}; //定时器容器
this.curId = 0; //定时器ID
};
Filter.prototype.before = function(msg, session, next) {
this.curId++;
this.timeouts[this.curId] = setTimeout(function() { //设置定时器,并将定时器的变量存到timeouts的容器里面
logger.warn('request %j timeout.', msg.__route__);
}, this.timeout);
session.__timeout__ = this.curId; //session 等级定时器的ID
next();
};
Filter.prototype.after = function(err, msg, session, resp, next) {
var timeout = this.timeouts[session.__timeout__]; //去除定时器的ID
if(timeout) {
clearTimeout(timeout); //清空定时器
delete this.timeouts[session.__timeout__];
}
next(err, msg);
};