天天看點

pomelo入門02

距離上一篇pomelo已經有一段時間了吧,總是有一些事情要做。。

這一篇來寫pomelo的application的建立過程。。當然例子還是以官方的chat。。。。主要是來分析下面這段代碼:

var app = pomelo.createApp();      

我們首先來看看pomelo的定義吧:

var fs = require('fs');
var path = require('path');
var application = require('./application');


/**
 * Expose `createApplication()`.
 *
 * @module
 */
//這裡相當于是進行命名
var Pomelo = module.exports = {};

/**
 * Framework version.
 */

Pomelo.version = '0.4';

/**
 * Event definitions that would be emitted by app.event
 */
Pomelo.events = require('./util/events');

/**
 * auto loaded components
 */
 //用于儲存元件
Pomelo.components = {};

/**
 * auto loaded filters
 */
 //用于儲存filter
Pomelo.filters = {};

/**
 * connectors
 */
 //connector對象
Pomelo.connectors = {};
//connector對象的get方法
Pomelo.connectors.__defineGetter__('sioconnector', function() {
  return require('./connectors/sioconnector');
});
//超級connector的get方法
Pomelo.connectors.__defineGetter__('hybridconnector', function() {
  return require('./connectors/hybridconnector');
});

/**
 * schedulers
 */
 //定義排程器
Pomelo.schedulers = {};
Pomelo.schedulers.__defineGetter__('direct', function() {
  var m = require('./scheduler/direct');
  return m;
});

Pomelo.schedulers.__defineGetter__('buffer', function() {
  return require('./scheduler/buffer');
});

/**
 * global channel manager
 */
Pomelo.connectors.__defineGetter__('redisGlobalChannelManager', function() {
  return require('./common/manager/redisGlobalChannelManager');
});

//用self來指向目前的作用域
var self = this;

/**
 * Create an pomelo application.
 *
 * @return {Application}
 * @memberOf Pomelo
 * @api public
 */
 //建立application對象
Pomelo.createApp = function (opts) {
  var app = application;
  app.init(opts);  //初始化函數
  self.app = app;
  return app;
};

/**
 * Get application
 */
 //相當于是為Pomelo對象添加一個屬性,app,它是一個對象,就存在一個get方法,用于傳回目前的app對象
Object.defineProperty(Pomelo, 'app', {
  get:function () {
    return self.app;
  }
});

/**
 * Auto-load bundled components with getters.
 */
 //用于在components檔案夾中讀取元件,然後儲存到components對象裡面去
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./components/' + name);
  }
  Pomelo.components.__defineGetter__(name, load);//例如執行Pomelo.components.aa,其實會得到require("./components/aa");的值
  Pomelo.__defineGetter__(name, load);
});

//用于讀取filter對象
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);
  }
  Pomelo.filters.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});
      

其實本身pomelo的定義還是很簡單的,無非就是定義了一些名字,然後加載了一些元件,這裡其實可以将pomelo對象看成已是一個資源的管理。。。

這裡大量用到了如下的方法:

Pomelo.connectors.__defineGetter__('redisGlobalChannelManager', function() {
  return require('./common/manager/redisGlobalChannelManager');
});      

這種用法其實就是定義一下這個屬性的擷取方法,當Pomelo.connectors.redisGlobalChannelManager的方式通路這個屬性的時候,會直接調用換進去的函數,得到傳回值。。。

我們重點來分析如下代碼:

//建立application對象
Pomelo.createApp = function (opts) {
  var app = application;
  app.init(opts);  //初始化函數
  self.app = app;
  return app;
};      

由于application的定義比較長,就不一次性列出來,先來看看它的init方法。。。。

Application.init = function(opts) {
  opts = opts || {};
  this.loaded = [];       // loaded component list
  //用于儲存所有的元件
  this.components = {};   // name -> component map

  //調用set方法儲存的參數将會儲存在這個地方
  this.settings = {};     // collection keep set/get
  this.set('base', opts.base);
  //事件對象
  this.event = new EventEmitter();  // event object to sub/pub events

  // current server info
  this.serverId = null;   // current server id
  this.serverType = null; // current server type
  this.curServer = null;  // current server info

  // global server infos
  this.master = null;         // master server info
  //用于儲存所有的server,id與info的鍵值對
  this.servers = {};          // current global server info maps, id -> info
  this.serverTypeMaps = {};   // current global type maps, type -> [info]
  this.serverTypes = [];      // current global server type list

  appUtil.defaultConfiguration(this);   //預設配置

  this.state = STATE_INITED;  //将目前的狀态修改為已經初始化
  logger.info('application inited: %j', this.getServerId());
};      

剛開始啟動的視乎,沒有參數傳進來,opts就是null,代碼本身也很簡單吧,可以當做是對application對象的一些屬性的聲明和初始化。。。

這裡主要是要看一下defaultConfiguration部分幹了些什麼。。。

我們來看看它的定義吧:

module.exports.defaultConfiguration = function (app) {
  var args = parseArgs(process.argv);  //這裡process.argv儲存的是運作使用的指令,是一個數組例如[ 'node', '/home/fjs/Desktop/pomelo/game-server/app.js' ]
  /*
  [ '/usr/local/bin/node',
  '/home/fjs/Desktop/pomelo/game-server/app.js',
  'env=development',
  'id=chat-server-2',
  'host=127.0.0.1',
  'port=6051',
  'serverType=chat' ]
*/

  //最後args如以下形式:
  //master { main: '/home/fjs/Desktop/pomelo/game-server/app.js' }

  /*{ main: '/home/fjs/Desktop/pomelo/game-server/app.js',
  env: 'development',
  id: 'connector-server-1',
  host: '127.0.0.1',
  port: 4050,
  clientPort: 3050,
  frontend: 'true',
  serverType: 'connector' }*/

  setupEnv(app, args);    //設定目前環境的配置,是開發,部署等
  loadMaster(app);    //加載master伺服器的配置
  loadServers(app);   //加載server的配置
  processArgs(app, args);
  configLogger(app);
};      

代碼還是很簡單吧,先是擷取目前的執行指令,例如:node app.js,

然後對其進行一些處理,将一些參數處理出來,這裡可能要分為兩種,畢竟有master伺服器和非master伺服器。。。他們解析出來的結果在上面的注釋都已經标了出來。。。

接下來就是設定環境,例如是development或者production。。。

然後載入Master伺服器的配置,然後載入普通server的配置。。。

這裡我們就選一個出來看看吧,看看普通server的配置是怎麼進行載入的。。。

var loadServers = function(app) {
  app.loadConfig('servers', app.getBase() + '/config/servers.json');  //載入server類型的伺服器的配置
  var servers = app.get('servers');   //擷取剛剛載入的配置資訊
  var serverMap = {}, slist, i, l, server;
  for(var serverType in servers) {   //周遊serve的類型,chat,gate,connector
    slist = servers[serverType];
    for(i=0, l=slist.length; i<l; i++) {    //周遊目前這種類型的server
      server = slist[i];
      server.serverType = serverType;
      serverMap[server.id] = server;   //将這些server儲存到serverMap裡面去

      if(server.wsPort) {
        logger.warn('wsPort is deprecated, ' +
                    'use clientPort and frontend instead.');
      }
    }
  }

  app.set('__serverMap__', serverMap);  //将所有的server儲存到app域裡去
};      

代碼還是很簡單的吧,首先載入配置檔案,然後周遊其中server類型,然後将他們用鍵值對管理起來就完事了。。。

額。。

最後還有一個  processArgs(app, args);

var processArgs = function(app, args){
  var serverType = args.serverType || 'master';   //如果沒有serverType,那麼就是master伺服器了
  var serverId = args.id || app.getMaster().id;    //擷取伺服器的id

  app.set('main', args.main, true);    //将運作資訊儲存起來,
    //master { main: '/home/fjs/Desktop/pomelo/game-server/app.js' }

  /*{ main: '/home/fjs/Desktop/pomelo/game-server/app.js',
  env: 'development',
  id: 'connector-server-1',
  host: '127.0.0.1',
  port: 4050,
  clientPort: 3050,
  frontend: 'true',
  serverType: 'connector' }*/
  app.set('serverType', serverType, true);
  app.set('serverId', serverId, true);

 if(serverType !== 'master') {  //儲存目前伺服器類型
    app.set('curServer', args, true);
  } else {
    app.set('curServer', app.getMaster(), true);   
  }
};      

其實這個名字有點唬人,并沒有執行什麼指令,無非是設定了一些參數而已,。。。。。

好了,那麼整個application的建立過程就差不多了,其實這部分最主要的事情還是将配置檔案load進來。。。

下一篇來分析一下啟動過程吧。