Pomelo App

应用程序配置,如何配置Pomelo框架?

Pomelo可以配置各个组件的选项,加载配置文件,开启Pomelo的特性等,这些配置都是在game-server/app.js文件中进行的。实际上在Pomelo的应用中有两个app.js,一个是在game-server目录下,一个是在web-server目录下。game-server下的app.js是整个游戏服务器的入口和配置点。web-server下的app.js是Web服务器入口。

应用程序配置

app.js是运行Pomelo项目的入口,在app.js文件中首先会创建一个app实例,这个app作为整个框架的配置上下文来使用,用户可以通过这个上下文,设置一些全局变量,加载配置信息等操作。

$ vim game-server/app.js
//加载pomelo
let pomelo = require("pomelo");
//创建app实例
let app = pomelo.createApp();
//通过app这个上下文对框架的配置以及一些初始化操作
app.configure(<env>, <serverType>, function(){});
app.configure(...);
app.set(...);
app.route(...);
//启动应用
app.start();

configure

使用app.configure调用来配置

服务器的配置主要由configure()方法完成,完整的app.configure配置参数格式:

app.configure([env], [serverType], [function]);
参数描述
env运行环境,可设置为developmentproductiondevelopment|production
serverType服务器类型,设置后只会对当前参数类型服务器做初始化,不设置则对所有服务器执行初始化的function。比如gateconnectorchat...
function具体的初始化操作,内部可以些任何对框架的配置操作逻辑。
Application.configure = function (env, type, fn) {
  var args = [].slice.call(arguments);
  fn = args.pop();
  env = type = Constants.RESERVED.ALL;

  if(args.length > 0) {
    env = args[0];
  }
  if(args.length > 1) {
    type = args[1];
  }

  if (env === Constants.RESERVED.ALL || contains(this.settings.env, env)) {
    if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type)) {
      fn.call(this);
    }
  }
  return this;
};

更多地可以在configure中针对不同的服务器、不同的环境,对框架进行不同的配置。这些配置包括设置一个上下文变量供应用使用,开启一些功能选项,配置加载一个自定义的组件component,针对不同的服务器,配置过滤器filter等配置操作。

loadConfig

例如:全局配置MySQL参数

$ vim game-server/config/mysql.json
{
  "development":
  {
    "host": "127.0.0.1",
    "port": "3306",
    "username": "root",
    "password": "root",
    "database": "pomelo"
  }
}

加载配置文件,用户通过loadConfig()加载配置文件后,加载后文件中的参数将会直接挂载到app对象上,可直接通过app对象访问具体的配置参数。

$ vim game-server/app.js
const path = require('path');
//全局配置
app.configure('production|development',  function(){
    //加载MySQL数据库
    app.loadConfig("mysql", path.join(app.getBase(), "config/mysql.json"));
    const host = app.get("mysql").host;//获取配置
    console.log("mysql config: host = %s",host);
});

用户可以使用loadConfig()的调用加载任何JSON格式的配置文件,用于其它的目的,并能通过app进行访问。需要注意的是所有的JSON配置文件中都需要指定具体的模式,也就是developmentproduction

/**
 * Load Configure json file to settings.
 *
 * @param {String} key environment key
 * @param {String} val environment value
 * @return {Server|Mixed} for chaining, or the setting value
 * @memberOf Application
 */
Application.loadConfig = function(key, val) {
  var env = this.get(Constants.RESERVED.ENV);
  val = require(val);
  if (val[env]) {
    val = val[env];
  }
  this.set(key, val);
};

上下文变量存取

上下文对象app提供了设置和获取应用变量的方法,签名为:

set

设置应用变量

app.set(name, value, [isAttach]);
参数描述
name变量名
value变量值
isAttach可选,默认为false,附加属性,若isAttach为true则将变量attach到app对象上作为属性。此后对此变量的访问,可直接通过app.name
/**
 * 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;
};

例如:

app.set("name", "project_name");
const name = app.get("name);//project_name
app.set("name", name, true);
const name = app.name;

get

获取应用变量

app.get(name);
/**
 * Get property from setting
 *
 * @param {String} setting application setting
 * @return {String} val
 * @memberOf Application
 */
Application.get = function (setting) {
  return this.settings[setting];
};

route

route主要负责请求路由信息的维护,路由计算,路由结果缓存等工作,并根据需要切换路由策略,更新路由信息等。

/**
 * Set the route function for the specified server type.
 *
 * Examples:
 *
 *  app.route('area', routeFunc);
 *
 *  var routeFunc = function(session, msg, app, cb) {
 *    // all request to area would be route to the first area server
 *    var areas = app.getServersByType('area');
 *    cb(null, areas[0].id);
 *  };
 *
 * @param  {String} serverType server type string
 * @param  {Function} routeFunc  route function. routeFunc(session, msg, app, cb)
 * @return {Object}     current application instance for chain invoking
 * @memberOf Application
 */
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;
};

用户可自定义不同服务器的不同路由规则,然后进行配置即可。在路由函数中,通过最后的回调函数中返回服务器的ID即可。

$ vim game-server/app.js
//聊天服务器配置
app.configure("production|development", "chat",function(){
    //路由配置
    app.route("chat", function(session, msg, app, cb){
        const servers = app.getServersByType("chat");
        if(!servers || servers.length===0){
            cb(new Error("can not find chat servers"));
            return;
        }
        const val = session.get("rid");
        if(!val){
            cb(new Error("session rid is not find"));
            return;
        }
        const index = Math.abs(crc.crc32(val)) % servers.length;
        const server = servers[index];
        cb(null, server.id);
    });
    //过滤配置
    app.filter(pomelo.timeout());
});

filter

实际应用中,往往需要在逻辑服务器处理请求之前对用户请求做一些前置处理,当请求被处理后又需要做一些善后处理,由于这是一种常见的情形。Pomelo对其进行了抽象,也就是filter。在Pomelo中filter分为before filter和after filter。在一个请求到达Handler被处理之前,可以经过多个before filter组成的filter链进行一些前置处理,比如对请求进行排队,超时处理。当请求被Handler处理完成后,又可以通过after filter链进行一些善后处理。这里需要注意的是在after filter中一般只做一些清理处理,而不应该再去修改到客户端的响应内容。因为此时,对客户端的响应内容已经发送给了客户端。

filter链

filter分为beforeafter两类,每个filter都可以注册多个形成一个filter链,所有客户端请求都会经过filter链进行处理。before filter会对请求做一些前置处理,如检查当前玩家是否已经登录,打印统计日志等。after filter是进行请求后置处理的地方,比如释放请求上下文的资源,记录请求总耗时等。after filter中不应该再出现修改响应内容的代码,因为在进入after filter前响应就已经被发送给客户端。

配置filter

当一个客户端请求到达服务器后,经过filter链和handler处理,最后生成响应返回给客户端。handler是业务逻辑实现的地方,filter则是执行业务前进行预处理和业务处理后清理的地方。为了开发者方便,系统内建提供了一些filter。比如serialFilter、timerFilter、timeOutFilter等,另外,用户可以根据应用的需要自定义filter。

app.filter(pomelo.filters.serial());

如果仅仅是before filter,那么调用app.before。


/**
 * Add before filter.
 *
 * @param {Object|Function} bf before fileter, bf(msg, session, next)
 * @memberOf Application
 */
Application.before = function (bf) {
  addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf);
};

如果是after filter,则调用app.after。

/**
 * Add after filter.
 *
 * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`
 * @memberOf Application
 */
Application.after = function (af) {
  addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af);
};

如果即定义了before filter,又定义了after filter,可以使用app.filter调用。

/**
 * add a filter to before and after filter
 *
 * @param {Object} filter provide before and after filter method.
 *                        A filter should have two methods: before and after.
 * @memberOf Application
 */
Application.filter = function (filter) {
  this.before(filter);
  this.after(filter);
};

用户可以自定义filter,然后通过app.filter调用,将其配置进框架。

filter对象

filter是一个对象,定义filter大致代码如下:

let Filter = function(){};
/**
 * 前置过滤器
 * @param msg 用户请求原始内容或经前面filter链处理后的内容
 * @param session 若在后端服务器上则是BackendSession,若在前端服务器则是FrontendSession
 * @param next
 */
Filter.prototype.before = function(msg, session, next){

};
/**
 * 后置过滤器
 * @param err 错误信息
 * @param msg
 * @param session
 * @param resp 对客户端的响应内容
 * @param next
 */
Filter.prototype.after = function(err, msg, session, resp, next){

};
module.exports = function(){
    return new Filter();
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值