天天看點

pomelo源碼解析之compnent元件啟動

pomelo中的各個功能子產品都是以component元件的形式封裝的。例如sessionService,channelService等等。

接下來我們來看component如何被啟動,廢話不多說,直接上源碼。

/**
 * Auto-load bundled components with getters.
 */
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');
  var _load = load.bind(null, './components/', name);

  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');
  var _load = load.bind(null, './filters/handler/', name);

  Pomelo.filters.__defineGetter__(name, _load);
  Pomelo.__defineGetter__(name, _load);
});

fs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');
  var _load = load.bind(null, './filters/rpc/', name);

  Pomelo.rpcFilters.__defineGetter__(name, _load);
});
           

首先我們應該了解pomelo.js這個檔案的具體作用—初始化所有的配置資訊和自動加載所有元件,上面的code就是将所有component已檔案名做為name,檔案加載得到的function作為value,儲存在pomelo對象中。

Application.load = function(name, component, opts) {
  if(typeof name !== 'string') {
    opts = component;
    component = name;
    name = null;
    if(typeof component.name === 'string') {
      name = component.name;
    }
  }

  if(typeof component === 'function') {
    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;
};
           

之後通過上面的代碼,将pomelo中的元件,執行component = component(this, opts);得到元件具體對象,并将對象儲存在this.components中 “this.components[name] = component;”。 此處的this就是app = pomelo.createApp();中的app對象。到此為止,app對象中已經儲存了我們所有需要component’元件,之後通過start函數,将每一個元件function調用得到元件對象并調用每一個元件對象的start函數。請大家往下看。

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() {
    appUtil.loadDefaultComponents(self);
    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();
    }
  });
};
           

此函數為application中的啟動函數,有pomelo繼承,當我們在app.js中做app.start()時,就是調用的此函數。

此處我們需要了解兩個函數appUtil.loadDefaultComponents(self); 和 appUtil.optComponents。

讓我們先來看loadDefaultComponents:

module.exports.loadDefaultComponents = function(app) {
  var pomelo = require('../pomelo');
  // load system default components
    logger.debug('lv:', pomelo)
  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'));
};
           

不知道還記不記得上面講到的,pomelo根據檔案名自動加載所有元件,我們可以看到app.load函數的第一個參數是不是在pomelo/lib/component目錄下都有這個檔案名。事實上,第一個參數就是一個元件函數,有興趣的還可以去看下app.load的具體實作。總之在這裡就将所有的元件函數做了調用得到了元件對象儲存在app.components當中。

接下來我們來看optComponents函數:

module.exports.optComponents = function(comps, method, cb) {
  var i = ;
  async.forEachSeries(comps, function(comp, done) {
    i++;
    if (typeof comp[method] === 'function') {
      comp[method](done);
    } else {
      done();
    }
  }, function(err) {
    if (err) {
      if(typeof err === 'string') {
        logger.error('fail to operate component, method: %s, err: %j', method, err);
      } else {
        logger.error('fail to operate component, method: %s, err: %j',  method, err.stack);
      }
    }
    utils.invokeCallback(cb, err);
  });
};
           

然後我們來看optComponents這個函數的代碼compmethod; 此處就是重點。相信大家已經猜到Constants.RESERVED.START 變量就是‘start’字元串。到了這裡pomelo的component啟動方式。非常清楚。

我們可以檢視pomelo的lib/componets目錄下所有的檔案都是固定的格式,都存在有start()函數,來作為啟動函數。

繼續閱讀