pomelo源碼分析(3)--配置設(shè)置和讀取及app.load

作者:shihuaping0918@163.com朋蔫,轉(zhuǎn)載請(qǐng)注明作者

https://github.com/NetEase/chatofpomelo/tree/master/game-server服務(wù)器為例子來(lái)說(shuō)明配置讀取是怎么完成的隔节,chatofpomelo是pomelo做為聊天服務(wù)器的例子。它的入口點(diǎn)是app.js嫂丙。代碼內(nèi)容如下:

var pomelo = require('pomelo');
var routeUtil = require('./app/util/routeUtil');
/**
 * Init app for client.
 */
var app = pomelo.createApp();
//這一行也關(guān)注一下
app.set('name', 'chatofpomelo'); 


// app configure
app.configure('production|development', function() {
    // route configures
    app.route('chat', routeUtil.chat);
//這一段代碼是我們要關(guān)心的
    app.set('connectorConfig', {
        connector: pomelo.connectors.sioconnector,
        // 'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
        transports: ['websocket', 'polling'],
        heartbeats: true,
        closeTimeout: 60 * 1000,
        heartbeatTimeout: 60 * 1000,
        heartbeatInterval: 25 * 1000
    });
    // filter configures
    app.filter(pomelo.timeout());
});

// start app
app.start();

//全局吃掉未處理異常
process.on('uncaughtException', function(err) {
    console.error(' Caught exception: ' + err.stack);
});

從代碼中看,都是通過(guò)app.set添加配置——這是pomelo比較不同的地方,常見(jiàn)的配置都是分成多個(gè)文件冯遂,然后通過(guò)include方式集中到一個(gè)文件脐往【慵茫或者就只有一個(gè)文件。但是pomelo的配置可以零散的這配一點(diǎn)钙勃,那配一點(diǎn)蛛碌。app.set這個(gè)函數(shù)的定義在application.js里面,內(nèi)容如下:

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 *
 * Example:
 *
 *  app.set('key1', 'value1');
 *  app.get('key1');  // 'value1'
 *  app.key1;         // undefined
 *
 *  app.set('key2', 'value2', true);
 *  app.get('key2');  // 'value2'
 *  app.key2;         // 'value2'
 *
 * @param {String} setting the setting of application
 * @param {String} val the setting's value
 * @param {Boolean} attach whether attach the settings to application
 * @return {Server|Mixed} for chaining, or the setting value
 * @memberOf Application
 */
Application.set = function (setting, val, attach) {
  if (arguments.length === 1) {
    return this.settings[setting];
  }
  this.settings[setting] = val;
  if(attach) {
    this[setting] = val;
  }
  return this;
};

這個(gè)函數(shù)的注釋寫得是非常 的詳細(xì)了辖源,相對(duì)我前面剛分析的那些c代碼來(lái)說(shuō)蔚携,那些代碼是基本沒(méi)注釋。從代碼中可以看出來(lái)克饶,實(shí)際上是把配置加到了settings里去了酝蜒。settings的初始化是setttings={},也就是初始是個(gè)空對(duì)象矾湃。

到這里配置的設(shè)置就分析完了亡脑,pomelo就是這么簡(jiǎn)單直接。配置的key是connectorConfig,value是一個(gè)js對(duì)象霉咨。下面看一下它是在哪里讀取的蛙紫。它一定是在Application.start里面讀取的。


/**
 * Start application. It would load the default components and start all the loaded components.
 *
 * @param  {Function} cb callback function
 * @memberOf Application
 */
 Application.start = function(cb) {
  this.startTime = Date.now();
  if(this.state > STATE_INITED) {
    utils.invokeCallback(cb, new Error('application has already start.'));
    return;
  }
  
  var self = this;
  appUtil.startByType(self, function() { // 先調(diào)startByType
    appUtil.loadDefaultComponents(self); //然后startByType回調(diào)從這開(kāi)始
    var startUp = function() {
      appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) {
        self.state = STATE_START;
        if(err) {
          utils.invokeCallback(cb, err);
        } else {
          logger.info('%j enter after start...', self.getServerId());
          self.afterStart(cb);
        }
      });
    };
    var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP];
    if(!!beforeFun) {
      beforeFun.call(null, self, startUp);
    } else {
      startUp();
    }
  });
};

appUtil.startByType這一篇不涉及途戒,就直接看回調(diào)了坑傅,回調(diào)第一行就是appUtil.loadDefaultComponents,這個(gè)函數(shù)會(huì)去讀設(shè)置喷斋。但是這個(gè)函數(shù)有個(gè)非常不好的行為唁毒,就是字符常量基本都是硬編碼,也就是大家常說(shuō)的星爪,在代碼里寫死浆西。如果不來(lái)看代碼,誰(shuí)也不知道這個(gè)配置就一定是要叫這個(gè)名字顽腾。


/**
 * Load default components for application.
 */
module.exports.loadDefaultComponents = function(app) {
  var pomelo = require('../pomelo');
  // load system default components
  if (app.serverType === Constants.RESERVED.MASTER) {
    app.load(pomelo.master, app.get('masterConfig'));
  } else {
    app.load(pomelo.proxy, app.get('proxyConfig'));
    if (app.getCurServer().port) {
      app.load(pomelo.remote, app.get('remoteConfig'));
    }
    if (app.isFrontend()) {
      app.load(pomelo.connection, app.get('connectionConfig')); 
//就是它室谚,在這里被加載了
      app.load(pomelo.connector, app.get('connectorConfig'));
      app.load(pomelo.session, app.get('sessionConfig'));
      // compatible for schedulerConfig
      if(app.get('schedulerConfig')) {
        app.load(pomelo.pushScheduler, app.get('schedulerConfig'));
      } else {
        app.load(pomelo.pushScheduler, app.get('pushSchedulerConfig'));
      }
    }
    app.load(pomelo.backendSession, app.get('backendSessionConfig'));
    app.load(pomelo.channel, app.get('channelConfig'));
    app.load(pomelo.server, app.get('serverConfig'));
  }
  app.load(pomelo.monitor, app.get('monitorConfig'));
};

分析到這里還沒(méi)完成,因?yàn)橹环治龅搅伺渲帽蛔x出來(lái)崔泵,讀出來(lái)以后的行為還沒(méi)有分析到秒赤。也就是說(shuō)這個(gè)配置到底用來(lái)干什么?這就要看app.load了憎瘸。app.load第一個(gè)參數(shù)叫pomelo.connector入篮,這個(gè)東西實(shí)際上是用__defineGetter__來(lái)做了一次函數(shù)的封裝,它實(shí)際上是一個(gè)函數(shù)幌甘。__defineGetter__不是標(biāo)準(zhǔn)里面的潮售。

/**
 * Load component
 *
 * @param  {String} name    (optional) name of the component
 * @param  {Object} component component instance or factory function of the component
 * @param  {[type]} opts    (optional) construct parameters for the factory function
 * @return {Object}     app instance for chain invoke
 * @memberOf Application
 */
Application.load = function(name, component, opts) {
  if(typeof name !== 'string') { //name是函數(shù)
    opts = component;  //參數(shù)移位
    component = name; //參數(shù)移位
    name = null;
    if(typeof component.name === 'string') {
      name = component.name;
    }
  }

  if(typeof component === 'function') { //移位后component是函數(shù)
    component = component(this, opts);
  }

  if(!name && typeof component.name === 'string') {
    name = component.name;
  }

  if(name && this.components[name]) {
    // ignore duplicat component
    logger.warn('ignore duplicate component: %j', name);
    return;
  }

  this.loaded.push(component);
  if(name) {
    // components with a name would get by name throught app.components later.
    this.components[name] = component;
  }

  return this;
};

所以在load這個(gè)函數(shù)里,name傳進(jìn)來(lái)實(shí)際是個(gè)函數(shù)锅风,對(duì)這個(gè)函數(shù)做調(diào)用酥诽,把opts作為參數(shù)傳進(jìn)去。這樣就實(shí)現(xiàn)了模塊的加載皱埠。

以pomelo.connector為例解釋一下模塊加載的過(guò)程肮帐,上面講到了pomelo.connector實(shí)際上是函數(shù),這是怎么實(shí)現(xiàn)的呢边器?首先到pomelo.js里去搜索训枢,肯定是找不到.connector的。因?yàn)樗峭ㄟ^(guò)下面這幾行代碼添加進(jìn)去的忘巧。在components目錄下有一個(gè)文件叫connector.js

/**
 * Auto-load bundled components with getters.
 */
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
//對(duì)于connector.js恒界,返回connector
  var name = path.basename(filename, '.js'); 
//這個(gè)bind下面再講
  var _load = load.bind(null, './components/', name);
  
  Pomelo.components.__defineGetter__(name, _load);
//看這里,__defineGetter__設(shè)置了砚嘴,當(dāng)訪問(wèn)pomelo.getter的時(shí)候十酣,
//調(diào)_load函數(shù)
  Pomelo.__defineGetter__(name, _load);
});

從代碼中可以看出來(lái)涩拙,當(dāng)pomelo.connector被訪問(wèn)時(shí),會(huì)被調(diào)用一個(gè)_load函數(shù)耸采。

下面再來(lái)看看這個(gè)load函數(shù)做了什么兴泥?load函數(shù)實(shí)際上是執(zhí)行了require,加載模塊洋幻。

function load(path, name) {
  if (name) {
    return require(path + name);
  }
  return require(path);
}

但是_load呢,是load.bind的結(jié)果翅娶。實(shí)際是把this指針設(shè)為null了文留。
bind的說(shuō)明:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

這里的require會(huì)返回一個(gè)函數(shù),require('connector')會(huì)返回函數(shù)竭沫≡锍幔看代碼吧,conpoment/connector.js里有一句話蜕提。

module.exports = function(app, opts) {
  return new Component(app, opts);
};

這個(gè)函數(shù)才是app.load真正調(diào)用的函數(shù)森书,也就是pomelo.connector返回的值,也就是require返回的值谎势。

所以app.load(pomelo.connector, app.get('connectorConfig'));這行代碼實(shí)際上是加載component/connector模塊凛膏,然后執(zhí)行模塊exports的函數(shù),將配置傳進(jìn)去脏榆,從而創(chuàng)建component猖毫。這個(gè)過(guò)程我是覺(jué)得已經(jīng)講得很詳細(xì)了,各位觀眾能不能理解就不好說(shuō)了须喂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吁断,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坞生,更是在濱河造成了極大的恐慌仔役,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件是己,死亡現(xiàn)場(chǎng)離奇詭異又兵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)卒废,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門寒波,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人升熊,你說(shuō)我怎么就攤上這事俄烁。” “怎么了级野?”我有些...
    開(kāi)封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵页屠,是天一觀的道長(zhǎng)粹胯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)辰企,這世上最難降的妖魔是什么风纠? 我笑而不...
    開(kāi)封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮牢贸,結(jié)果婚禮上竹观,老公的妹妹穿的比我還像新娘。我一直安慰自己潜索,他們只是感情好臭增,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著竹习,像睡著了一般誊抛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上整陌,一...
    開(kāi)封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天拗窃,我揣著相機(jī)與錄音,去河邊找鬼泌辫。 笑死随夸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的震放。 我是一名探鬼主播逃魄,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼澜搅!你這毒婦竟也來(lái)了伍俘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勉躺,失蹤者是張志新(化名)和其女友劉穎癌瘾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體饵溅,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妨退,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜕企。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咬荷。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖轻掩,靈堂內(nèi)的尸體忽然破棺而出幸乒,到底是詐尸還是另有隱情,我是刑警寧澤唇牧,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布罕扎,位于F島的核電站聚唐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏腔召。R本人自食惡果不足惜杆查,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臀蛛。 院中可真熱鬧亲桦,春花似錦、人聲如沸浊仆。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)氧卧。三九已至桃笙,卻和暖如春氏堤,著一層夾襖步出監(jiān)牢的瞬間沙绝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工鼠锈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闪檬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓购笆,卻偏偏與公主長(zhǎng)得像粗悯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子同欠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容