论MMORPG游戏服务器架构

就我见到过的MMO服务器架构做一个总结吧。

第一种,靠一个matchmaking服务器来做负荷均衡,这种架构比较适合大厅游戏或者副本游戏。方法是用一个服务器作为登陆服务器,所有玩家请求连接的要求都被该服务器处理,除了将玩家分配到相对空闲的服务器外,还要处理玩家的特殊搜索需求,譬如等级在多少之间的,同样需要完成什么副本任务的等等,将这么一群合适的人一起分配在同一个服务器。这样的优点就是服务器资源得到了最大的利用,玩家彼此负载均衡,还可以顺带处理一些复杂的匹配逻辑。缺点也很明显,这种架构不适合大场景游戏使用。


第二种,一个服务器负责一个游戏世界。如果不打算支持大世界,或者只针对一个小规模的项目,这么做也没什么不好,依照现在的服务器硬件,撑个几百上千人应该问题不大。


第三种,根据场景的不同划分不同的服务器,也就是说,每个服务器负责一部分场景。这应该也是比较原始的一种方法吧,可以处理大型MMORPG的需求,保证所有的玩家都在一个相对“完整”的服务器上游戏,可以处理大量的交互。这种方法的优点是把负荷进行了平均划分,保证了多人可以在同一个游戏环境,缺点是,因为服务器划分的时候并不知道哪个场景的人多,哪个场景的人少,所以导致的问题就是,可能负责人多的那个服务器会很卡,但是在一些边缘人比较少的场景,游戏就顺畅一些。隐约记得当年传奇好像就是这样的一个情况。从大的意义上来说,跟第二种没有啥太大的本质区别。


第三种,动态均衡服务器。关于这种方式,做法其实很多,就目前手上这个项目的做法,其实很独特,因为游戏本身是一个超级大的无缝场景,跟魔兽世界一样,用一个服务器来处理所有数据是不可能的(既然场景都这么大了,如果限制玩家上限就实在是美中不足),所以这里必须考虑多态服务器共同支持一个游戏世界。我们的做法是,有一个服务器会处理登陆信息,然后查看哪个服务器比较空闲,这一点其实跟第一种是一样的。当玩家被分配到相对空闲的不同服务器以后,他们要彼此交互怎么办呢(譬如,这个服务器的A和那个服务器的B站在一起,他们必须能彼此看见和相互交互)。这个时候,当A需要与B交互时,它会向一个服务器发送请求,当这个特定的服务器处理这个需求的时候,他会仲裁,到底是把A传送到B上,还是把B传送到A上,总之,任意两个玩家要处理交互的时候,都必须通过申请,然后在服务器的一个异步回调里继续处理交互(类似于,我申请,我要和B交互啦,然后服务器通过仲裁,将玩家同步到一个服务器,然后告诉申请者,你们俩在一个服务器啦,你要做啥就做啥吧)。如果两个玩家本身就在一个服务器,那么他们的同步请求会处理得非常快(1个逻辑帧),如果不在,那么就要承受数据在服务器之间传送的成本,大概也就几毫秒还是十几毫秒我不记得数据了。原理就是这么个样子,也就是说,如果两个玩家在不同的服务器上,但是他们的位置站在一起,他们还要PK,那么就是一个天大的杯具了。再设想,如果N个玩家不凑巧全在N个服务器,他们要一起PK,这里成本就会有点大,不过都还好,因为同步请求只会被处理一次,等他们都同步到一个服务器上的时候就不卡了(按照最坏的情况,三十个玩家同时pk,延迟也不过500毫秒不到,所以问题应该不大)。现在来讲一个真正的杯具,那就是。。。。城战!作为一个大型MMORPG并且是无缝地图,处理大规模城战是必须的一个问题。当大量玩家聚在一起要处理交互的时候,由于他们所在的服务器不同,所以这个时候仲裁服务器会非常的忙,首先将不参加城战的人移动到空闲的服务器上,将所有需要参加城战的人移动到同一个服务器上。这里没有办法准确的知道一个玩家是否参与城战,比如,当一个玩家参加城战,另外一个在其他服务器看热闹的玩家一砍刀给他砍过去,他们瞬间有了交互,于是仲裁服务器就会考虑,是把空闲服务器的玩家移动到城战服务器上,还是相反。这里需要一个非常好的算法来处理。


就我自己的看法,其实开销还可以减少,也就是现在比较普遍的镜像,就算B和A不在一个服务器上,但是他们在彼此的服务器上都有一个数据镜像,当A需要和B交互的时候,他不会等B的数据同步过来,而是直接取本地镜像的值,而本地镜像的值是在不定时更新的,譬如B的数据发生了更改,他就会广播一个消息给服务器群,然后所有收到消息的服务器就更新本地镜像数据。


关于这两种不同的做法,第一种的优点是,他可以得到最即时的数据,但是这里要承担一笔同步的开销,在最坏的情况下,这笔开销可能并不会很小。但是我个人觉得这样做最大的缺点是:将服务器逻辑彻底搞异步了。作为逻辑服务器,异步是非常棘手的一件事情,因为逻辑应该是一个流形态的执行方式,结果执行到处理交互的地方,就会被请求中断,然后又只有把接下来的逻辑写到消息回调函数里面。别说debug很痛苦,就是处理各种情况下的回调也是一件很棘手的事情,读代码也不是很舒服,并且有些逻辑地方你必须要考虑到会处理交互和不会处理交互两种情况,所以代码结构会比较难以控制。


第二种方法基本可以避免第一种异步的问题,但是呢,因为每个玩家都有一个镜像剩余在所有服务器上,所以这里可能会增加一些服务器负担用于管理这些镜像,如果玩家特别多,那么这种开销也会相应的增大,再加上消息广播,这里也不容小觑。而且你拿到的信息并不是最新的,所以你获得的结果未必是即时的。这样导致的问题是,可能在不同的服务器上,运算的结果可能会不一样。毕竟在广播消息更新镜像的地方,其实也是异步的,有的服务器先得到更新,他的处理就和后面没有得到更新通知的服务器不一样。

对于这一点,我的看法是,完全可以容忍这种误差。实际上,这种看似逻辑上天大的误差,玩家是根本感觉不到的。从概念上来看,除了当前玩家以外的所有角色对于他来说,都是一个镜像,镜像到底同步不同步,到底和服务器数据是否匹配,对于他来说都是透明的。只要保证所有的客户端看起来都是一样的,这种客户端和服务器的偏差完全可以忽略。

设想:有一个关键数据玩家A的HP值,当在另外一个服务器上的B准备和A pk的时候,B检测到A的HP是10,给他造成的伤害是30。当B计算伤害结果的那一瞬间,A喝了一瓶药,HP变成了50.那么对于B来说,A应该挂掉,对于A来说,他还可以承受这次攻击。这里就有一个矛盾,如何来解决?我个人的看法,其实这里有A同步给其他的服务器的消息:我的HP变成50了,也有B同步给其他服务器的消息:A的HP变成0了。每个消息都应该有个时间戳,我们让时间的先后来决定最后的结果即可。当其他服务器接到两种不同的消息结果的时候,直接就用最新的那个时间的数据为准,实际上,对于大部分其他玩家来说,A的HP到底是多少,他们根本不关心。如果杯具一点,两个消息发生的时间是一模一样的,那咱再加一个潜规则在里面,比如比较消息包里某个地址的数据内容。反正决策的办法很多,只要保证所有服务器所得到的结果是一样就ok,消息来到的先后并不是什么问题。


这种异步的做法其实好处很多,之前我写的一个3D世界的寻路服务器,大概也是依照这种思路,其他一些异步需求比如,计算玩家视野,数据排序等等,都可以用这种服务器异步的方式来完成。因为逻辑本身就是一个异步的过程。


总结了一下大概就这么多,因为并不是资深的服务器开发人员,难免有一些错误,有问题的地方还请指正。


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值