(十五)nodejs循序渐进-高性能游戏服务器框架pomelo之Protobuf模块

消息压缩 

在实际编程中,为了减少数据传输带宽的消耗,提高传输效率,pomelo提供了对消息的压缩,包括基于字典的对route的压缩和基于protobuf的对具体传输数据的压缩。

route压缩

在实际编程中,网络带宽的有效数据负载率是一个值得考虑的问题。特别地,对于移动客户端来说,网络资源往往并不是很丰富,为了尽可能地节省网络资源,往往需要尽大可能地增加数据包的有效数据率。

route问题

在pomelo编程中,pomelo中的route是用来确定消息的分发路径,将其交给相应的服务器和服务处理的。route分为两类,由客户端发给服务端消息时使用的route和服务端向客户端广播时使用的route。

  • 前一种route是由服务器自动生成的,其中的字段就代表了对应的方法在服务端的位置。如“area.playerHandler.attack”则表示在“area”类型的服务器上的“playerHandler”提供的“attack”方法,其格式为".."。 路由信息过长,使得有效消息数据负载率大大降低。例如,在聊天应用中,如果用户的发言仅仅是一个字符,结果不得不携带一个route,"chat.chatHandler.send",这样使得有效数据负载率大大降低。

  • 后一种route是服务端想客户端推送消息时使用,是客户端的路由信息,如“onMove”,“onAttack”等,其格式一般为"on"这些字段是由用户自己定义的。虽然可以定义很短的路由,但是那样会造成可读性变差,不利于代码阅读。

一般来说,当应用固定后,具体路由就不会再变动,因此可以考虑通过一种简单替换的方式对路由信息进行压缩。

基于dict的压缩

pomelo中实现了基于字典的route压缩,目前route压缩功能仅仅支持hybridconnector,sioconnector目前无法使用route压缩。其实现原理如下:

  • 对于系统生成的route,也就是服务端的路由信息,即格式为".."的路由信息,在系统启动时由CoDictionary组件进行服务端路由信息扫描,然后会对每一个route生成唯一的字典项,由一个无符号小整数标识。

  • 对于用户自定义的route,也就是客户端的路由信息,即格式为"on",则需要用户提供一个自定义的route列表,会根据这一个列表对每个用户自定义的route生成一个对应的字典项,即也就是一个无符号小整数。

  • 在开启字典功能的状态下,使用hybridconnector的时候,当协议握手的时候,服务端会将整个字典的消息发送给客户端,这样客户端和服务端都会拥有相同的具体route无符号整数的对应关系。

  • 当有消息传递时,其中的route在发送时会被替换为在字典项,而接收端会自动还原,这一过程对于用户而言是完全透明的。

pomelo的protobuf实现,借助了javascript的动态性,使得应用程序可以在运行时解析proto文件,不需要进行proto文件的编译。

pomelo的实现中,为了更方便地解析proto文件,使用了json格式,与原生的proto文件语法是相通的,但是是不相同的。 

使用protobuf

虽然protobuf的实现看上去十分复杂,但由于这一层对用户是完全透明的,使用会非常简单。用户只需要通过简单的两步定义就可以在原有的项目中开启protobuf功能。

  • 首先,需要在connector组件上打开protobuf开关,在app.js中的配置如下:
var Configure = function() {
  app.set('name', 'treasures');

  app.configure('production|development', 'gate', function() {
    app.set('connectorConfig', {
      connector: pomelo.connectors.hybridconnector
    });
  });

  app.configure('production|development', 'connector', function() {
    app.set('connectorConfig', {
      connector: pomelo.connectors.hybridconnector,
      heartbeat: 100,
      useDict: true,
      useProtobuf: true
    });
  });

  app.configure('production|development', 'area', function() {
    var areaId = app.get('curServer').areaId;
    if (!areaId || areaId < 0) {
      throw new Error('load area config failed');
    }

    var areaService = bearcat.getBean('areaService');
    var dataApiUtil = bearcat.getBean('dataApiUtil');
    areaService.init(dataApiUtil.area().findById(areaId));
  });
}
  • 实际上需要加入的就是“useProtobuf:true”这一项。当设置这一标识后,pomelo会在客户端握手时将protos内容同步到客户端,并默认开启protobuf压缩功能。

  • 在protobuf功能开启用,用户还需要加入protos定义来实现对具体消息的编码/解码。protos文件默认在/game-server/config目录下,包括两个文件:serverProtos.json和clientProtos.json,分别表示服务端->客户端消息的protos和 客户端->服务端消息的protos。只要在其中加入有效的proto定义,就可以开启对应消息的protobuf编码功能,CoProtobuf组件会自动加载这两个proto文件。比如这样定义一个serverProtos.json

    {
      "onPickItem" : {
        "required uInt32 entityId" : 1,
        "required uInt32 target" : 2,
        "required uInt32 score" : 3
      },
      "rankUpdate" : {
        "repeated uInt32 entities" : 1
      }
    }
  • 当然pomelo中的protobuf实现对原有项目是完全兼容的,你可以直接在老的项目中打开protobuf开关而不会引起任何问题。只是当proto定义是空的,默认所有的消息都不会经过protobuf压缩,而是采用默认的二进制编码进行传输。

  • 当你想对某个消息进行protobuf编码时,只需要在对应的protos文件(serverProtos.json或clientProtos.json)中加入对应的protobuf项,pomelo在启动时就会自动识别并对消息进行压缩,而不会对其他未定义的消息产生任何影响。

      那么我提供游戏里的一部分源代码,大家可以看下如何使用proto:

  这里在pushMessage里,route配置的是serverProto.json里的onPickItem,那么就会按照onPickItem的格式来生成编码,里边的字段包含了下面赋值的部分。

player.on('pickItem', function(args) {
    var player = self.getEntity(args.entityId);
    var treasure = self.getEntity(args.target);
    player.target = null;
    if (treasure) {
      player.addScore(treasure.score);
      self.removeEntity(args.target);
      self.getChannel().pushMessage({
        route: 'onPickItem',
        entityId: args.entityId,
        target: args.target,
        score: treasure.score
      });
    }
  });

 

已标记关键词 清除标记
相关推荐
实付 39.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值