数据服务器的设计

今天开始正式开始做数据服务器。在这点上,我希望一组服务器上所有的逻辑服务遇到的数据存取需求都通过一个单一的数据服务器完成。而且,写入数据是单向通讯的。即,逻辑服务器只提读写盘请求,而无须确认。写数据好说,读数据稍微难处理一点,我现在的方案是,数据服务器加载数据的部分只对位置服务器负责,把数据提交到位置服务器即可。位置服务器可以通过分析数据知道玩家的数据流应该流向哪台逻辑服务器。

以上逻辑是基于每个玩家有独立的数据的,而且一个玩家同时只存在于唯一一个场景。也就是说,当一组数据存活的时候,只唯一属于一台逻辑服务器。这样做的好处是,切换场景非常的简单,只是让玩家从一个场景退出,给数据服务器发出写盘指令,并发送所有数据。数据服务器写盘的同时也 cache 了这些数据,并向位置服务器提交新的位置,并把这些数据转发向位置服务器。位置服务器可以再转交给新的场景。

对于玩家登陆和登出的处理并没有特别之处,我们可以设立两个虚拟场景,一个负责登入,一个负责登出。每个新连接自动导入登入场景,这个场景负责发出加载指令(下面可以看到,甚至无须设置加载数据的协议)。然后再做一个自动的场景切换操作即可。而玩家登出,则是转入登出场景,这是一个黑洞,玩家的连接可以在这里安全的断掉。

当玩家不停的穿梭于各个场景之间时,为了避免频繁的数据转发,我们可以给玩家数据做一个赃标记。如果没有弄赃,实际的数据可以不被转发。这个标记的另一个意义在于,若数据没有弄赃,而数据服务器的 cache 中又没有数据时,就需要从外存加载了。其实这就是一个读请求。

月初我在公司内部讲解这个设计时,遭到了一些同事的疑问。最典型的一个是,帮派信息如何处理。的确,类似帮派的信息,不属于任何一个玩家。如果你单独设一个非玩家对象保存这些数据时,可能会分布到不同的逻辑服务器上。的确对数据服务器的设计是一个挑战。

今天仔细考虑过以后,我发现可以从设计上避免这个问题。方法如下:

以帮派为例,可以设一个帮派总坛的场景。这个场景可以是虚拟的,在 client 表现上甚至可以只有一个界面,进入这个场景的玩家之间可以相互不可见。这样减少了大量的通讯量。

玩家任何时候查询,修改,帮派数据的时候,都强制他进入这个场景工作(也就是独立进程维护唯一数据)。做完操作后,玩家可以从哪来的回哪去。表现上,他可能并不会察觉到自己进入了一个遥远的地方。

甚至很多玩法应蕴而生。比如帮派在各地开了分舵。组织要求你把总坛的物质运输到分舵。其实仓库里的数据都是放在一个进程中。表现上,玩家可能千里迢迢的押运货物,只是修改了一下物品的归属地而已。

帮派也可以开出空头支票,玩家接受支票交易时,或许要去帮派回所去查一下帮派的信誉度,或者验证一下支票能不能兑现。因为支票开出去到了玩家手上后,数据已经交给玩家个人管理,和帮派数据无关了。

这样,复制出来的帮派数据都有了时延,比如你不能及时得到帮主是谁,帮派里有什么变化等等。(只是举例,实际万一想解决,可以走聊天这种不可靠通道)但是我个人认为这是符合现实的。因为现实中要得知一件事情,也是依赖一个信息发生源的。

想通了这些后,个人感觉我们的设计最终还是保持了简洁。满符合 KISS 原则。每个进程好好干好一件事吧 :)

ps. 这里也突出了心跳服务器的重要。许多虚拟场景,如帮派管理等,可以放慢它的心跳(至少不用处理战斗时 10Hz 那么高的频率),这样也降低了这些进程的负载。而玩家体验却并不会有太大的影响。

评论