接着(一)所分析的代碼
// 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 <[email protected]>
* 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);
};