数据广播方案的优化

来源:互联网 发布:c语言null包含头文件 编辑:程序博客网 时间:2024/05/29 07:51

在服务器组的架构下,我们一般会引入一个网关服务器,或类似功能的组件,所有的客户端连接都是到这里,数据然后转发给当前所在的地图服务器。

 

这样,在数据广播时便存在一个很大的优化可能性。以前的单服务器架构时,比如要广播移动消息,可以直接找出周围的玩家列表,构造要发送的数据,然后依次调用send即可。但是在多服务器架构下要是还这么做的话,那地图服务器与网关服务器之间的数据传输量将会非常大,而且这些数据之间除了目标IP地址不一样外,实际内容完全相同。

 

其实在以前单服务器架构时就曾考虑过该优化方案。最初使用的立即发送数据包的方式在遇到需要同时发送大量数据时出现了问题,为了避免由于在逻辑线程内的send调用导致的游戏逻辑被阻塞,我们将数据发送工作放到了一个独立的线程中,游戏逻辑线程在需要向客户端发送数据时,只是将要发送的数据包和客户端连接句柄递交给了发包线程。这个过程也就和带网关的多服务器架构完全类似了。

 

当时也是为了避免给发包线程递交太多的请求,因为每个发包请求都需要拷贝一次数据包并添加到发送队列中,显而易见的弊端就是数据多次拷贝的CPU消耗和队列中存在多份数据的内存消耗,所以优化的必要性非常高。

 

最终采取的方案是只递交一次发包请求,在请求包里面包含了这个数据包要发送到的客户端句柄列表,这样数据完全不需要做拷贝,而且内存占用也只有一份。

 

放到多服务器架构下也可以这样做,区别仅在于发包请求是发送给了网关服务器。

 

 

以前的方案只做到了这一步,再继续考虑一下,其实还有进一步优化的可能。

 

拿比较简单的聊天数据包来说,比如在一个小组频道内聊天,服务器在广播此类数据包时,每次递交的发包请求中的客户端句柄列表都是相同的,除非队员发生变动。所以,可以考虑的是这个列表其实不用每次都发送。通过控制命令在网关服务器上先建立好这些广播组,以后广播数据时只需要指明广播组编号即可。在云风的《游戏服务器内的组播》一文中对此有介绍。

 

这里的组我们也可以称之为频道,比如小组频道,团队频道,公会频道,世界频道,本地频道,当前频道等,当然还可能会有自建的频道,每个频道有一个唯一ID。不同玩家间的当前频道需要独立,但其他频道可以共用。

 

关于当前频道需要特别说明一下。这里的当前频道指的是以玩家当前所在位置为中心点的一个可见范围,也就是当玩家移动,或者说话时需要广播到的范围。因为玩家位置是经常会变动的,所以这个频道内的玩家列表变动也非常频繁,而且不同玩家间的当前频道不能像小组频道一样进行共用。

 

这个方案对于玩家列表变动不频繁的组队聊天这类情况很有效,但是对于玩家列表会频繁变动的当前频道广播却有些麻烦,维护这类频道会使得地图服务器与网关服务器之间的频道成员管理命令非常频繁。

 

但是这里也有个选择,一是在频道成员发生变化时立即向网关服务器通知变动,另外一种做法是只在有频道数据要递交时才检查有无成员变动。

 

比如一个玩家坐着没有动,不停有玩家从其旁边经过,这时他的当前频道玩家列表是不断变化的,但如果该玩家不做任何操作,比如移动和在当前频道聊天,这个变动情况其实是不需要反馈到网关服务器的,因为不会有这个频道内的数据需要广播。

 

当然,如果这样做的话,可能就需要在地图服务器上也保留两份当前频道玩家列表,用于比较该列表的变动情况,这也就是要在内存占用和网络数据传输量上做个权衡。虽然未经实践验证,目前来说我还是比较倾向于后一种方案。

------------------------------------------

顺便对上一篇文章再点补充说明

上一篇说到网络游戏位置的同步策略时,对于3D游戏的位置同步方法没有做过多的描述,其实是有考虑,只是最后未写出来。

3D游戏中以按键来移动位置时,很直观的一个想法是,当按下键的时候广播当前状态,当停止按键或者按键状态发生改变时再广播一下,这样发送的数据量就比较小了。

但是在实际的编码及考虑之后,发现这种情况有个问题,服务器不能直接获知客户端当前的确切位置,但是服务器却又是随时可能需要知道每个玩家的当前真实位置的,比如一个AOE技能的使用,服务器需要计算哪些玩家在该范围之内。

如果采用这种移动同步方案的话,就要求服务器为每个玩家运行移动的模拟,而且要以一定的频率来不停更新当前位置。这种模拟一是CPU消耗太大,二是模拟出来的位置并不能确保与客户端的当前真实位置完全一致,所以最终考虑的结果是,还不如就让客户端自己以一定频率来报告自己的位置来的实在。

这也正是WOW的做法,只要按下了移动键,客户端就以10HZ的频率不停向服务器报告自己的当前位置及移动方向状态,服务器再将这个信息向周围玩家广播。

 
原创粉丝点击