其他分享
首页 > 其他分享> > pomelo 框架第一部分

pomelo 框架第一部分

作者:互联网

pomelo 框架第一部分

项目地址: https://github.com/NetEase/chatofpomelo-websocket.git
分支:tutorial-protobuf

一、pomelo.js

pomelo.js中的初始化

Pomelo.connectors = {};
Pomelo.connectors.__defineGetter__('sioconnector', load.bind(null, './connectors/sioconnector'));
Pomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector'));
Pomelo.connectors.__defineGetter__('udpconnector', load.bind(null, './connectors/udpconnector'));
Pomelo.connectors.__defineGetter__('mqttconnector', load.bind(null, './connectors/mqttconnector'));

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

defineGetter 方法可以将一个函数绑定在当前对象的指定属性上,当那个属性的值被读取时,你所绑定的函数就会被调用。
通过 defineGetterload.bind 给Pomelo.connectors绑定相应属性,如:sioconnector、hybridconnector、udpconnector、mqttconnector。

pomelo/lib/components

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);
});

pomelo/lib/filters/handler

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);
});

pomelo/lib/filters/rpc

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);
});

通过读取相应文件夹下的文件,根据文件的名字绑定到相应的属性上面。

二、项目启动

1.creaeApp方法

app.js 中,var app = pomelo.createApp();调用pomelo.js 中的Pomelo.createApp方法:

Pomelo.createApp = function (opts) {
  var app = application;
  app.init(opts);
  self.app = app;
  return app;
};

通过createApp方法,对application进行初始化,并返回app实例。

2.app.init(opts)

application.jsApplication.init
① 设置初始值
② 调用appUtil.defaultConfiguration(this);读取配置
③ 设置this.state = 1

3.appUtil.defaultConfiguration(this)

/ util / appUtil.jsdefaultConfiguration

var args = parseArgs(process.argv); // main: 'chatofpomelo-websocket/game-server/app.js'
setupEnv(app, args); // 设置服务器的运行环境
loadMaster(app); // 加载master.json文件
loadServers(app); // 加载servers.json文件
processArgs(app, args); // 根据进程参数设置app属性
configLogger(app); // 加载log4js.json
loadLifecycle(app); // 加载生命周期

4.app.js中加载配置

app.js
app.configure('production|development', 'connector', function(){ // 运行环境、服务器类型
	app.set('connectorConfig', // 属性名称
		{
			connector : pomelo.connectors.hybridconnector, // connector类型
			heartbeat : 3,	// 心跳时间
			useDict : true, // 是否开启字典
			useProtobuf : true // 是否开启Protobuf
		});
});

根据参数配置,设置connectorConfig属性到app上,如上:connector类型选择了hybridconnector,心跳时间为3秒,开启字典以及protobuf。

app.js
app.configure('production|development', function() {
	app.route('chat', chatRoute); // 设置chat服务器的分配route规则
    app.filter(pomelo.timeout()); // 添加超时过滤器
});

根据参数配置,设置chat服务器的路由分配规则,并添加超时过滤器。
如果未指定路由分配规则,则按pomelo默认路由分配规则进行路由分配。

application.js
Application.route = function(serverType, routeFunc) {
  var routes = this.get(Constants.KEYWORDS.ROUTE);
  if(!routes) {
    routes = {};
    this.set(Constants.KEYWORDS.ROUTE, routes);
  }
  routes[serverType] = routeFunc;
  return this;
};

根据不同的服务器类型,给__routes__属性绑定路由分配规则。

proxy.js
var defaultRoute = function(session, msg, app, cb) {
  var list = app.getServersByType(msg.serverType);
  if (!list || !list.length) {
    cb(new Error('can not find server info for type:' + msg.serverType));
    return;
  }
  var uid = session ? (session.uid || '') : '';
  var index = Math.abs(crc.crc32(uid.toString())) % list.length;
  utils.invokeCallback(cb, null, list[index].id);
};

此为默认路由分配规则,在proxy.js初始化时进行指定路由分配规则,根据__routes__属性中是否有自定义的路由分配,进行不同的赋值。

app.js
app.configure('production|development', 'chat', function() {
  app.filter(abuseFilter());
});

为chat服务器添加自定义过滤器abuseFilter。

application.js
Application.filter = function (filter) {
  this.before(filter);
  this.after(filter);
};

Application.before = function (bf) {
  addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf);
};

Application.after = function (af) {
  addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af);
};

var addFilter = function(app, type, filter) {
 var filters = app.get(type);
  if(!filters) {
    filters = [];
    app.set(type, filters);
  }
  filters.push(filter);
};

pomelo过滤器分为前置过滤器和后置过滤器,handler在before和after之间,根据配置过滤器,指定到app的__befores__和__afters__上。

5.app.start

app.start();调用application的start方法。

二、application.start

1.application.start

application.js
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.loaded中组件的start方法
                self.state = STATE_START;
                if (err) {
                    utils.invokeCallback(cb, err);
                } else {
                    logger.info('%j enter after start...', self.getServerId());
                    self.afterStart(cb); // 调用application的afterStart方法
                }
            });
        };
        var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP];
        if (!!beforeFun) {
            beforeFun.call(null, self, startUp);
        } else {
            startUp();
        }
    });
};

application.start的功能主要是,加载默认组件,启动组件,所有组件启动完成后,改变application的state属性为2,然后调用afterStart方法,继续后续调用。

2.appUtil.loadDefaultComponents(self);

appUtil.js
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'));
};

loadDefaultComponents根据服务器类型,加载不同的组件。

master 服务器
加载 master组件和 monitor组件
非 master 服务器
加载 proxy 组件、 backendSession 组件、 channel 组件和 server 组件
app.getCurServer().port: 加载 remote 组件
app.isFrontend: 加载 connection 组件、 connector 组件、 session 组件和 pushScheduler 组件

3. application.load

application.js
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); // 加载到loaded中
    if (name) {
        // components with a name would get by name throught app.components later.
        this.components[name] = component; // 加载到components中
    }

    return this;
};

初始化相应组件,然后把组件添加到 app.loadedapp.components 属性中。

4.appUtil.optComponents

module.exports.optComponents = function (comps, method, cb) {
    var i = 0;
    async.forEachSeries(comps, function (comp, done) {
        i++;
        if (typeof comp[method] === 'function') { // 如果此模块中包含method方法,执行
            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);
    });
};

appUtil.optComponents执行相应的method方法。

5.application.afterStart

Application.afterStart = function (cb) {
    if (this.state !== STATE_START) {
        utils.invokeCallback(cb, new Error('application is not running now.'));
        return;
    }

    var afterFun = this.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTUP];
    var self = this;
    appUtil.optComponents(this.loaded, Constants.RESERVED.AFTER_START, function (err) { // 调用self.loaded中组件的afterStart方法
        self.state = STATE_STARTED;
        var id = self.getServerId();
        if (!err) {
            logger.info('%j finish start', id);
        }
        if (!!afterFun) {
            afterFun.call(null, self, function () {
                utils.invokeCallback(cb, err);
            });
        } else {
            utils.invokeCallback(cb, err);
        }
        var usedTime = Date.now() - self.startTime;
        logger.info('%j startup in %s ms', id, usedTime);
        self.event.emit(events.START_SERVER, id); // emit start_server 监听
    });
};

application.afterStart调用app.loadedafterStart方法,执行完后,emit start_server 监听

三、模块启动 lib/components

1.backendSession.js

初始化BackendSessionService,设置backendSessionServicelocalSessionService
name = __backendSession__

var service = new BackendSessionService(app);
app.set('backendSessionService', service, true);
app.set('localSessionService', service, true);

2.channel.js

初始化ChannelService,设置channelService
name = __channel__

var service = new ChannelService(app, opts);
app.set('channelService', service, true);

3.connection.js

用于前端服务器统计连接状态的连接组件(connection)。
初始化ConnectionService,把ConnectionService上的方法除了startstop绑定到this.service
name = __connection__

this.app = app;
this.service = new ConnectionService(app);

// proxy the service methods except the lifecycle interfaces of component
var method, self = this;

var getFun = function (m) {
    return (function () {
        return function () {
            return self.service[m].apply(self.service, arguments);
        };
    })();
};

for (var m in this.service) {
    if (m !== 'start' && m !== 'stop') {
        method = this.service[m];
        if (typeof method === 'function') {
            this[m] = getFun(m);
        }
    }
}

4.connector.js

连接器组件(connector), 接收客户端请求并使用套接字附加会话。
name = __connector__

this.connector = getConnector(app, opts); // 初始化connector
this.encode = opts.encode; // 编码
this.decode = opts.decode; // 解码

if (opts.useDict) { // 字典
	app.load(pomelo.dictionary, app.get('dictionaryConfig')); // 加载字典
}

if (opts.useProtobuf) { // protobuf
	app.load(pomelo.protobuf, app.get('protobufConfig')); // 加载protobuf
}

app.js中对connectorConfig属性进行了赋值,application 调用start时调用了appUtil中的loadDefaultComponents方法,在app.loaded时取connectorConfig属性,对connector进行初始化。

connector : pomelo.connectors.hybridconnector

因为connectorhybridconnector,所以getConnector(app, opts)是对hybridconnector进行初始化,即:connector = hybridconnector(clientPort, host, opts)。

5.dictionary.js

读取'/config/dictionary.json'配置文件,对其this.userDicPath属性进行赋值。
name = __dictionary__

6.master.js

初始化master
name = __master__

var Master = require('../master/master');
this.master = new Master(app, opts);

6.monitor.js

初始化monitor
name = __monitor__

var Master = require('../monitor/monitor');
this.monitor = new Monitor(app, opts);

7.protobuf.js

读取'/config/serverProtos.json''/config/clientProtos.json'配置文件,对protobuf进行初始化操作。
name = __protobuf__

8.proxy.js

代理组件。为rpc客户端生成代理。
name = __proxy__

获取proxyConfig属性中的值,对proxy进行初始化赋值操作。

opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;
opts.interval = opts.interval || 30;
opts.router = genRouteFun();
return new Component(app, opts);

获取__routes__属性,如果在app.js中有自定义操作,那根据自定义规则进行赋值,如果没有自定义,则按默认方法进行赋值

var genRouteFun = function () {
    return function (session, msg, app, cb) {
        var routes = app.get('__routes__'); // 获取__routes__属性
        if (!routes) {
            defaultRoute(session, msg, app, cb);
            return;
        }
        var type = msg.serverType, route = routes[type] || routes['default'];
        if (route) {
            route(session, msg, app, cb); // 自定义路由
        } else {
            defaultRoute(session, msg, app, cb); // 默认路由
        }
    };
};

this.client进行初始化,进而监听ADD_SERVERSREMOVE_SERVERSREPLACE_SERVERS

var Component = function(app, opts) {
  this.app = app;
  this.opts = opts;
  this.client = genRpcClient(this.app, opts); // require('pomelo-rpc').client
  this.app.event.on(events.ADD_SERVERS, this.addServers.bind(this));
  this.app.event.on(events.REMOVE_SERVERS, this.removeServers.bind(this));
  this.app.event.on(events.REPLACE_SERVERS, this.replaceServers.bind(this));
};

9.pushScheduler.js

调度程序组件,用于调度消息发送。
name = __pushScheduler__
获取调度算法,可自定义,如果没有自定义,即默认调度方法

this.scheduler = getScheduler(this, app, opts);

10.remote.js

远程服务的组件,加载远程服务并添加到全局上下文。
name = __remote__

获取remoteConfig属性中的值,对remote进行初始化赋值操作。

11.server.js

服务器启动组件。
name = __server__

this.server = Server.create(app, opts);

初始化server/server.js

12.session.js

session组件,管理session
name = __session__

初始化SessionService,把SessionService上的方法除了startstop绑定到this.service

this.service = new SessionService(opts);

以上组件中:
mastermonitorproxyremotechannelserverdictionaryconnector中包含start

标签:__,function,框架,第一,app,js,pomelo,var,name
来源: https://blog.csdn.net/ai_milk/article/details/114259122