pomelo之master服务器的启动

来源:互联网 发布:eclipse 查看java源码 编辑:程序博客网 时间:2024/05/22 08:09

写完前面的两篇文章,一直走的都是master服务器的流程,那么这一篇就真正涉及到master服务器的启动过程了,在真正开始之前,先回顾一下前面的两篇文章。。

(1)创建app的过程,这部分主要要完成的功能是读入用户定义的配置参数,并保存和处理这些配置参数。

(2)启动app的过程,这部分主要要完成的功能是load组件,完成对组件的包装(前面已经对master组件进行了说明,在真正的master外面还有一个master的包装,代理它的启动等过程)


新来看看那个master的代理吧:

/** * Component for master. */var Master = require('../master/master');/** * Component factory function * * @param  {Object} app  current application context * @return {Object}      component instances */ //相当于require之后得到的就是这个函数module.exports = function (app) {  return new Component(app);};/*** Master component class** @param {Object} app  current application context*/var Component = function (app) {  this.master = new Master(app);   //创建爱你master};var pro = Component.prototype;/** * Component lifecycle function * * @param  {Function} cb * @return {Void} */pro.start = function (cb) {  this.master.start(cb);};/** * Component lifecycle function * * @param  {Boolean}   force whether stop the component immediately * @param  {Function}  cb * @return {Void} */pro.stop = function (force, cb) {  this.master.stop(cb);};
就像前面说的一样,它就是一个外包装而已。。。

那么我们来看真正的master,首先来看看真正的master的构造:

//用于创建mastervar Server = function(app) {  this.app = app;  this.masterInfo = app.getMaster();   //master的配置实在loadMaster的配置的时候读取的,这个时候获取master的配置  this.registered = {};   //  this.modules = [];  //所有的模块  //Default is the main master  this.isSlave = false;  this.masterConsole = admin.createMasterConsole({   //创建console    port: this.masterInfo.port   //master的端口  });  if(app.enabled('masterHA')){ //Zookeeper这部分暂时先搁置,因为以前完全不了解它    this.zookeeper = Zookeeper.getClient(app);  }};
珍格格构造的过程看起来还是比较的简单,这里对于zookeeper的部分就先搁置,以后再来看,因为以前完全没有涉及到过zookeeper。

前面的代码要是读入master的配置信息,然后创建master的console,那么接下来来看这个console是怎么创建的吧:

module.exports.createMasterConsole = function(opts) {opts = opts || {};opts.master = true;   //用于表示当前是master服务器return new ConsoleService(opts);  //创建并返回console};
好像这部分代码也没啥意思,好吧接着来看:
var ConsoleService = function(opts) {EventEmitter.call(this);   //让当前的对象可以处理事件this.port = opts.port;   //获取端口号this.values = {};this.master = opts.master;   //当前是否是master服务器this.modules = {};if(this.master) {this.agent = new MasterAgent(this);   //构造master agent} else {this.type = opts.type;this.id = opts.id;this.host = opts.host;this.agent = new MonitorAgent({consoleService: this,id: this.id,type: this.type,info: opts.info});}};
好像代码还是很简单,主要还是要创建MasterAgent,好吧,我们再来看它的创建过程吧:
var MasterAgent = function(consoleService) {  EventEmitter.call(this);  this.consoleService = consoleService;   //console  this.server = null;  this.idMap = {};  this.typeMap = {};  this.clients = {};  this.reqId = 1;  this.callbacks = {};  this.state = ST_INITED;};util.inherits(MasterAgent, EventEmitter);
好吧,没啥意思,那么我们接下来来看看master的start的过程吧,希望能有更多有意义的东西。。。

那么接下来来看master的start函数吧,它的代码比较长一些,需要一部分一部分的进行分析:

Server.prototype.start = function(cb) {  registerDefaultModules(this.app);   //注册默认的modules  loadModules(this, this.masterConsole);   //载入module  var self = this;  this.masterConsole.start(function(err) {   //启动console    if(err) {      cb(err);      return;    }    startModules(self.modules, function(err) {  //启动module      if(err) {        cb(err);        return;      }      //If it is the back up, do note start server      if(!self.app.enabled('masterHA')){        logger.info('masterHA not enabled, start servers');        starter.runServers(self.app);  //启动其余的server        cb();      }else{        self.zookeeper.start(function(err, result){          if(err){            logger.error('start zookeeper failed! err : %j', err);            cb(err);            return;          }          self.zookeeper.getLock(function(err, result){            if(err || !result){              self.isSlave = true;              self.zookeeper.on('onPromote', self.onPromote.bind(self));              cb();            }else{              self.zookeeper.setData(self.masterInfo, function(err){                if(err){                  logger.error('set master info faild!');                  cb(err);                  return;                }                starter.runServers(self.app);                cb();              });            }          });        });      }    });  });  this.masterConsole.on('disconnect', function(id, type, reason) {  //设置disconnect事件    crashLogger.info(util.format('[%s],[%s],[%s],[%s]', type, id, Date.now(), reason || 'disconnect'));    var server = self.app.getServerById(id);    if(!!server && server['auto-restart'] === 'true') {      self.app.addServers(server);      starter.run(self.app, server, function(err) {        if(err) {          cb(new Error("could not restart " + server.serverId + err), null);          return;        }      });    }  });  this.masterConsole.on('register', function(record) {  //register事件    starter.bindCpu(record.id, record.pid, record.host);  });};
其实大意已经很清楚了,首先register模块,然后load模块,接着启动上面创建的console,然后再启动模块,然后再启动用户定义的其余的server,最后还要设置一些事件的处理函数。。。。

好了,那么首先来看看module的register的过程吧:

var registerDefaultModules = function(app) {  app.registerAdmin(require('../modules/watchdog'), {app: app, master: true});  app.registerAdmin(require('../modules/console'), {app: app, starter: starter});  if(app.enabled('systemMonitor')) {    app.registerAdmin(admin.modules.systemInfo);    app.registerAdmin(admin.modules.nodeInfo);    app.registerAdmin(admin.modules.monitorLog, {path: pathUtil.getLogPath(app.getBase())});    app.registerAdmin(admin.modules.scripts, {app: app, path: pathUtil.getScriptPath(app.getBase())});    if(os.platform() !== 'win32') {      app.registerAdmin(admin.modules.profiler, {isMaster: true});    }  }};
好像也没有什么意思吧,看看:
Application.registerAdmin = function(moduleId, module, opts){  var modules = this.get('__modules__');  if(!modules) {    modules = [];    this.set('__modules__', modules);  }  if(typeof moduleId !== 'string') {    opts = module;   //这个是传进的参数    module = moduleId;    moduleId = module.moduleId;  }  modules.push({moduleId: moduleId, module: module, opts: opts});  //将它push进去};
这部分其实还是相对比较的简单吧,无非是载入模块,并设置待会会用到的参数,那么接下来:
var loadModules = function(self, consoleService) {  // load app register modules  var modules = self.app.get('__modules__'); //获取module的信息  if(!modules) {    return;  }  var record, moduleId, module;  for(var i=0, l=modules.length; i<l; i++){   //遍历所有的module    record = modules[i];    if(typeof record.module === 'function') {  //一般情况下,这里都是一个函数,因为module的定义直接弄成了一个函数,可以看成构造函数      module = record.module(record.opts, consoleService);//可以看成调用module的构造函数    } else {      module = record.module;    }    moduleId = record.moduleId || module.moduleId;    if(!moduleId) {      logger.warn('ignore an uname module.');      continue;    }    consoleService.register(moduleId, module);  //在console里面注册module,在console里面会将id和module关联起来保存    self.modules.push(module);  }};
其实这部分的代码还是很简单的,首先遍历所有的module,然后调用构造函数,创建这些module,最后还要讲这些module注册到console里面去,在console里面会将这些module与其的id关联起来进行保存。。。

那么这里module的register和load过程基本就差不多了,至于说这些module有什么用,还是留到以后涉及到了再说吧。。。


好吧,我们接下来来看看console的启动过程:

ConsoleService.prototype.start = function(cb) {if(this.master) {this.agent.listen(this.port);  //监听端口exportEvent(this, this.agent, 'register');  //如果agent发生了register事件,那么这里也要调用一次exportEvent(this, this.agent, 'disconnect');process.nextTick(function() {utils.invokeCallback(cb);  //调用回调函数});} else {logger.info('try to connect master: %j, %j, %j', this.type, this.host, this.port);this.agent.connect(this.port, this.host, cb);exportEvent(this, this.agent, 'close');}exportEvent(this, this.agent, 'error');for(var mid in this.modules) {this.enable(mid);  //遍历并enable当前所有保存的module,在master的loadmodule的过程,会将这些module保存到console里面来}};
这里首先会让agent来listen用户配置的master端口(socket.io),这里还有比较有意思的地方就是设置了一些事件,如果agent发生了,那么console相应的也会发生一次,类似javascript的DOM事件的冒泡的过程,从里面冒到外面来。。。估计这种设计想法也是有模仿的意思吧。。。接着在调用定义的回调函数,在回调函数中将会启动module和其余的server。。。这与说agent里的listen究竟干了些什么事情,这里也就先搁着一下吧,因为现在的所有看到的执行流程中还是没有设计到这部分。。。。


好了接下来来看看module的start过程吧:

var startModules = function(modules, cb) {  // invoke the start lifecycle method of modules  if(!modules) {    return;  }  startModule(null, modules, 0, cb);};var startModule = function(err, modules, index, cb) {  if(err || index >= modules.length) {    cb(err);    return;  }  var module = modules[index];  if(module && typeof module.start === 'function') {    module.start(function(err) {      startModule(err, modules, index + 1, cb);  //我晕,这里居然还是递归的进行start    });  } else {    startModule(err, modules, index + 1, cb);  }};
好像也没啥意思吧,说白了就是调用module的start函数,然后这里有个比较有意思的就是,start的过程居然还要递归的进行,至于说为什么这样,呵呵,不知道。。


那么接下来来看是怎么进行其余的server的启动吧:

 starter.runServers = function(app) {  var servers = app.getServersFromConfig();  for (var serverId in servers) {  //遍历所有的server    this.run(app, servers[serverId]);  //启动server  }};
遍历所有的server,然后启动,这里server的信息说白了就是用户定义的server,这些信息在前面已经载入了进来。。

starter.run = function(app, server, cb) {  env = app.get('env');  //当前环境,development或者production  var cmd, key;  if (isLocal(server.host)) {  //host配置信息    var options = [];    if (!!server.args) {      if(typeof server.args === 'string') {        options.push(server.args.trim());      } else {        options.push(server.args);      }    }    cmd = app.get('main');  //用于启动给的主要信息      /*{ 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' }*/    options.push(cmd);    options.push(util.format('env=%s',  env));  //当前的环境    for(key in server) {  //将server的配置信息输入到命令启动行,例如host,port等      if(key === 'cpu') {        cpus[server['id']] = server[key];      }      options.push(util.format('%s=%s', key, server[key]));    }    starter.localrun(process.execPath, null, options, cb);  //运行命令行  } else {    cmd = util.format('cd "%s" && "%s"', app.getBase(), process.execPath);    var arg = server.args;    if (arg !== undefined) {      cmd += arg;    }    cmd += util.format(' "%s" env=%s ', app.get('main'), env);    for(key in server) {      if(key === 'cpu') {        cpus[server['id']] = server[key];      }      cmd += util.format(' %s=%s ', key, server[key]);    }    starter.sshrun(cmd, server.host, cb);  }};

这部分代码仿佛看起来挺复杂的,其实不然,因为大多数在前面的代码中都有涉及,无非是将要执行的命令行处理出来,然后待会用这些命令行参数来进行启动。。。那么就不细说了,直接来看localrun函数吧:
 //直接启动命令行starter.localrun = function (cmd, host, options, callback) {  logger.info('Executing ' + cmd + ' ' + options + ' locally');  spawnProcess(cmd, host, options, callback);};

那么其余server的启动也就差不多了,当然这部分还有一个插曲,那就是这里server的启动还需要分时本地服务器的,还是外地服务器的,其实看代码也就启动的过程有稍微的区别,别的也都差不多,就不细说了。。。


好了,那么整个master的启动过程大概就如下:

(1)创建masterconsole

(2)创建masteragent

(3)注册以及载入module

(4)启动console
(5)启动module

(6)启动其余的server


这里有一张图感觉应该能形容master的构成:


其实也就是说master服务器大多数的功能都是通过masterconsole进行的,而masterconsole又包含一个masteragent用于监听端口,以及一些处理。。

当然至于说master服务器的具体运行原理这里文章中并没有涉及,以后会补上,因为现在确实还不知道master干些什么事情。。。

原创粉丝点击