pomelo源碼分析(二)

接着(一)所分析的代碼

// 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);
};


發佈了112 篇原創文章 · 獲贊 37 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章