pomelo源码分析(3)--配置设置和读取及app.load
来源:互联网 发布:gta5卡怎么优化 编辑:程序博客网 时间:2024/05/22 15:38
作者:shihuaping0918@163.com,转载请注明作者
以https://github.com/NetEase/chatofpomelo/tree/master/game-server服务器为例子来说明配置读取是怎么完成的,chatofpomelo是pomelo做为聊天服务器的例子。它的入口点是app.js。代码内容如下:
var pomelo = require('pomelo');var routeUtil = require('./app/util/routeUtil');/** * Init app for client. */var app = pomelo.createApp();//这一行也关注一下app.set('name', 'chatofpomelo'); // app configureapp.configure('production|development', function() { // route configures app.route('chat', routeUtil.chat);//这一段代码是我们要关心的 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 appapp.start();//全局吃掉未处理异常process.on('uncaughtException', function(err) { console.error(' Caught exception: ' + err.stack);});
从代码中看,都是通过app.set添加配置——这是pomelo比较不同的地方,常见的配置都是分成多个文件,然后通过include方式集中到一个文件。或者就只有一个文件。但是pomelo的配置可以零散的这配一点,那配一点。app.set这个函数的定义在application.js里面,内容如下:
/** * 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;};
这个函数的注释写得是非常 的详细了,相对我前面刚分析的那些c代码来说,那些代码是基本没注释。从代码中可以看出来,实际上是把配置加到了settings里去了。settings的初始化是setttings={},也就是初始是个空对象。
到这里配置的设置就分析完了,pomelo就是这么简单直接。配置的key是connectorConfig,value是一个js对象。下面看一下它是在哪里读取的。它一定是在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() { // 先调startByType appUtil.loadDefaultComponents(self); //然后startByType回调从这开始 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这一篇不涉及,就直接看回调了,回调第一行就是appUtil.loadDefaultComponents,这个函数会去读设置。但是这个函数有个非常不好的行为,就是字符常量基本都是硬编码,也就是大家常说的,在代码里写死。如果不来看代码,谁也不知道这个配置就一定是要叫这个名字。
/** * 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'));};
分析到这里还没完成,因为只分析到了配置被读出来,读出来以后的行为还没有分析到。也就是说这个配置到底用来干什么?这就要看app.load了。app.load第一个参数叫pomelo.connector,这个东西实际上是用__defineGetter__
来做了一次函数的封装,它实际上是一个函数。__defineGetter__
不是标准里面的。
/** * 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是函数 opts = component; //参数移位 component = name; //参数移位 name = null; if(typeof component.name === 'string') { name = component.name; } } if(typeof component === 'function') { //移位后component是函数 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这个函数里,name传进来实际是个函数,对这个函数做调用,把opts作为参数传进去。这样就实现了模块的加载。
以pomelo.connector为例解释一下模块加载的过程,上面讲到了pomelo.connector实际上是函数,这是怎么实现的呢?首先到pomelo.js里去搜索,肯定是找不到.connector的。因为它是通过下面这几行代码添加进去的。在components目录下有一个文件叫connector.js
/** * Auto-load bundled components with getters. */fs.readdirSync(__dirname + '/components').forEach(function (filename) { if (!/\.js$/.test(filename)) { return; }//对于connector.js,返回connector var name = path.basename(filename, '.js'); //这个bind下面再讲 var _load = load.bind(null, './components/', name); Pomelo.components.__defineGetter__(name, _load);//看这里,__defineGetter__设置了,当访问pomelo.getter的时候,//调_load函数 Pomelo.__defineGetter__(name, _load);});
从代码中可以看出来,当pomelo.connector被访问时,会被调用一个_load函数。
下面再来看看这个load函数做了什么?load函数实际上是执行了require,加载模块。
function load(path, name) { if (name) { return require(path + name); } return require(path);}
但是_load呢,是load.bind的结果。实际是把this指针设为null了。
bind的说明:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
这里的require会返回一个函数,require(‘connector’)会返回函数。看代码吧,conpoment/connector.js里有一句话。
module.exports = function(app, opts) { return new Component(app, opts);};
这个函数才是app.load真正调用的函数,也就是pomelo.connector返回的值,也就是require返回的值。
所以app.load(pomelo.connector, app.get('connectorConfig'));
这行代码实际上是加载component/connector模块,然后执行模块exports的函数,将配置传进去,从而创建component。这个过程我是觉得已经讲得很详细了,各位观众能不能理解就不好说了。
- pomelo源码分析(3)--配置设置和读取及app.load
- 【Pomelo源码分析】2016-09-21 程序入口(app.js, pomelo.js, application.js)
- pomelo源码分析(一)
- pomelo源码分析(二)
- pomelo源码分析(三)
- pomelo源码分析(四)
- pomelo源码分析(五)
- pomelo源码分析(六)
- pomelo源码分析(8)--session
- 【Pomelo源码分析】2016-09-20 入门(pomelo命令)
- Pomelo聊天室源码分析(一)
- 如何配置及读取App.config文件
- pomelo源码分析(4)--connector之网络监听
- pomelo源码分析(5)--node.js中的this
- pomelo源码分析(6)--connector协议处理message
- pomelo源码分析(7)--connector与其它组件交互
- opencart中的load类和Registry类及controller分析
- samba源码分析及配置详解
- java实现定时任务的三种方法
- java实现FTP文件上传与文件下载
- Mips KVM Trap&Emulate implemented in Linux
- 【Java POI】1、Java POI的使用
- D25 Scala基础
- pomelo源码分析(3)--配置设置和读取及app.load
- fopen
- spring 配置定时任务
- 如何使用C语言实现人员信息展示
- D26 Scala增强
- Java-Eclipse安装教程
- D27 Akka实现,及自定义RPC
- EventBus(二)之源码分析
- 内核中的page fault & copy_from_user