(十五)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});}});

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444501.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++:13---多态和虚函数表

多态的意思为“以一个public基类的指针/引用,寻址一个派生类对象”。 “多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定。这是如何实现的呢?请看下面的程序,该程序演示了多态类对象存储空间的大小。 #in…

leetcode96. 不同的二叉搜索树 动归vs数学?

给定一个整数 n&#xff0c;求以 1 ... n 为节点组成的二叉搜索树有多少种&#xff1f; 示例: 输入: 3 输出: 5 解释: 给定 n 3, 一共有 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 …

Redis:06---数据库管理

一、服务器中的数据库Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中&#xff0c;db数组的每个项都是一个redis.h/redisDb结构&#xff0c;每个redisDb结构代表一个数据库&#xff1a;struct redisServer {// ...redisDb *db; // 一个数组&#…

leetcode95. 不同的二叉搜索树 II

给定一个整数 n&#xff0c;生成所有由 1 ... n 为节点所组成的二叉搜索树。 示例: 输入: 3 输出: [ [1,null,3,2], [3,2,null,1], [3,1,null,null,2], [2,1,3], [1,null,2,null,3] ] 解释: 以上的输出对应以下 5 种不同结构的二叉搜索树&#xff1a; 1 3 …

在同一局域网下连接共享文件夹失败,提示:你不能访问共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问

1.尝试打开guest访问。 &#xff08;1&#xff09;使用键盘 win R 键&#xff0c;打开运行窗口&#xff0c;并输入 gpedit.msc 打开本地组策略编辑器窗口 &#xff08;2&#xff09;选择计算机配置------->管理模板-------->网络-------->Lanman工作站。 &#…

(十五)深入浅出TCPIP之Hello CDN

什么是CDNCDN 其实是 Content Delivery Network 的缩写&#xff0c;即“内容分发网络”。CDN是将媒体资源&#xff0c;动静态图片(Flash) &#xff0c;HTML, CSS, JS等等内容缓存到距离你更近的互联网数据中心&#xff0c;从而让用户进行共享资源&#xff0c;实现缩减站点间的响…

Redis:07---Redis数据结构

一、五大数据结构Redis可以存储键与5种不同数据结构类型之间的映射&#xff0c;这5种数据结构类型分别为&#xff1a;STRING&#xff1a;字符串LIST&#xff1a;列表SET&#xff1a;集合HASH&#xff1a;散列ZSET&#xff1a;有序集合TYPE命令用来获得键的数据类型&#xff0c;…

C++:14---虚继承,虚函数,多态

一、多级混合继承 下面先介绍菱形继承 //菱形继承 class A { public: int data; }; class B:public A { public: int data; }; class C:public A { public: int data; }; class D:public B,public C { public: int data; };int main() { D c; D.data=1; D.B::data=2;//访问B中的…

(十四)nodejs循序渐进-高性能游戏服务器框架pomelo之开发Treasures游戏

#Tutorial 2 -- Treasures ##描述 Treasures 游戏是从 LordOfPomelo 中抽取出来&#xff0c;去掉了大量的游戏逻辑&#xff0c;用以更好的展示 Pomelo 框架的用法以及运作机制。 Treasures 很简单&#xff0c;输入一个用户名后&#xff0c;会随机得到一个游戏角色&#xff0c;…

leetcode243. 最短单词距离(vip题)好像挺简单?

给定一个单词列表和两个单词 word1 和 word2&#xff0c;返回列表中这两个单词之间的最短距离。 示例: 假设 words ["practice", "makes", "perfect", "coding", "makes"] 输入: word1 “coding”, word2 “practice”…

谈谈苹果应用内支付(IAP)的坑

一、请求商品 下面是请求商品的代码: - (void)validateProductIdentifier:(NSArray *)productIdentifier {SKProductsRequest *productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:productIdentifier]];self.request = productRe…

leetcode204. 计数质数(vip题)

统计所有小于非负整数 n 的质数的数量。 示例: 输入: 10 输出: 4 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 思路&#xff1a;筛法&#xff0c;见代码。 class Solution {public int countPrimes(int n) {// 1. 给数加上标记byte[] nums new byte[n];for (i…

如何使得客户端和服务器端完美配合做IOS应用内付费

配置Developer.apple.com 登录到Developer.apple.com,然后进行以下步骤: 为应用建立建立一个不带通配符的App ID用该App ID生成和安装相应的Provisioning Profile文件。配置iTunes Connect 登录到iTunes Connet,然后进行以下步骤: 用该App ID创建一个新的应用。在该应用中…

IOS内购流程从0-1手把手教会

苹果掌握着可能是全球最重要的APP分发渠道,然而30%的抽成近年来也被人批评,现在苹果似乎也看到反对意见了,从2021年1月1日开始,部分小型企业的分成费用降低到15%。 据报道,苹果将于2021年1月1日启动App Store小企业项目,会降低他们的抽成费用。针对年收入不足100万美元的…

leetcode217. 存在重复元素(vip题)超简单

给定一个整数数组&#xff0c;判断是否存在重复元素。 如果任何值在数组中出现至少两次&#xff0c;函数返回 true。如果数组中每个元素都不相同&#xff0c;则返回 false。 示例 1: 输入: [1,2,3,1] 输出: true 示例 2: 输入: [1,2,3,4] 输出: false 示例 3: 输入: [1,1,…

订单数据持久化和验证相关解决方案

订单数据持久化 有时候苹果支付在支付完成后,从苹果服务器返回收据的过程中可能会掉单(可能是网络问题,可能是苹果BUG,也有一部分是开发者自身埋的坑),因此我们需要一个订单持久化的机制来保障。 首先根据内购商品ID(此商品ID是在苹果后台建好的内购商品)、用户信息(…

IOS iap处理逻辑流程图再次梳理

序言: 本文补全一下iOS iap处理逻辑。 iap处理逻辑 苹果退单wiki:https://developer.apple.com/documentation/storekit/in-app_purchase/handling_refund_notifications 一、上图主要处理了以下业务: 普通购买 自动续订订阅 补单处理 预防黑产 退单处理 二、除了上述业…

(十七)深入浅出TCPIP之HTTP和HTTPS

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&…

leetcode283. 移动零 比官方更好的解法。

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作&#xff0c;不能拷贝额外的数组。 尽量减少操作次数。 思路&#xff1a;记录0的个…

C++:15---异常机制

1.概念:异常处理是一种允许两个独立开发的程序组件在程序执行时遇到不正常的情况相互通信的工具 2.异常检测和异常处理的方式throw表达式:程序遇到了错误或者无法处理的问题,使用throw引发异常try、catch语句块:以关键字tyr开始,并以一个或多个catch子句结束。它们也被称为…