跳至主要內容

原神的游戏服务器架构是怎么样的?

小白debug大约 12 分钟

原神的游戏服务器架构是怎么样的?

现在最火的游戏「原神」,它的玩法有点像「塞尔达旷野之息」,是故意的还是不小心?

你可能无所吊谓,但如果换成你最爱的王者农药或者吃鸡战场,又或者是你最烦的渣渣辉代言的一刀 999呢?

那么问题来了,你知道它们的游戏服务器架构是怎么样的吗?

今天我们从单机游戏开始聊起,让大家了解下网络游戏架构的演化过程,最后看看大家最关心的原神游戏服务器架构是怎么样的。

单机游戏

小时候我爸斥"巨资"给我买了个小霸王,希望我能通过它学会怎么用五笔输入法在键盘打字。

不出意外,打字我是一丁点没学会,但却对「超级玛丽」哪一关的角落里有变身蘑菇了然于心。

后来家里有了电脑后,我学会了玩「暴力摩托」,「侠盗猎车」。

它们都有个共同点,不需要联网,因此,我们一般称它为单机游戏

本质上,它们也是计算机进程的一种。

通过进程里的代码逻辑,我们能在游戏世界内探索地图的每个角落,能拾取道具,还能跟怪物对战,释放技能。

所有的游戏逻辑都在用户的电脑里,因此,就算没网,用户也一样能玩游戏。

单机游戏
单机游戏

但这样就有个问题,正因为所有的代码逻辑都在用户那,因此用户就可以通过修改进程数据来达到作弊的效果。比如,当角色挂了的时候,观察哪部分进程数据发生了变化,我们就大概能猜出内存里哪个地方是控制复活次数的,将它改大,你就能实现无限续命。我相信你肯定玩过99 条命版本的「魂斗罗」和「超级玛丽」。


网络游戏

表面上看,作弊貌似会让玩家更爽,但那都是短期的,它其实影响了游戏的公平性,长期来看,它会让用户流失的更快,进而影响游戏营收。

因此作为开发者,我们得让用户改不了一些核心数据,那最好的办法就是将核心数据逻辑从用户侧挪出去,放到远端服务器里。

于是,游戏就被分成了两部分,一部分是游戏画面,负责玩家交互,画面渲染等,这就是游戏客户端。另一部分负责游戏角色的核心玩法逻辑和数据,这就是游戏服务端,放游戏核心逻辑数据的那个服务器,我们叫它GameServer

于是游戏架构成了这样。玩家客户端通过网络直连GameServer

最简单的网络游戏架构
最简单的网络游戏架构

但我们总不能给每个玩家都给配一个GameServer,因此GameServer需要支持多人同时在线

多人同时在线游戏
多人同时在线游戏

这就是最简单的网络游戏架构。早期的网络游戏,以及现在很多简单的小程序游戏服务端都是这么做的。

但这样有个很大的问题,因为游戏这块蛋糕很大,所以总会遇到很多挺刑的事情。

如果让用户直连游戏服务端,那相当于把游戏服务端的 ip暴露给了所有人。

不赚钱还好,一旦游戏赚钱,就会遇到各种攻击。

你猜《羊了个羊》最火的时候为啥老是崩溃?

假设一个GameServer能承载 4k 个玩家,服务器内存储着 4k 个游戏玩家当前的游戏数据状态,一旦服务器遭受直接攻击,那 4k 个玩家都会被影响,并且这类服务器承载了大量游戏逻辑,因此启动需要加载各种地图,商品,怪物等各种配置,会很慢,每一秒玩家都在流失,每一次崩溃都可能让老板的宝马变青桔

这攻击的是服务器吗?这明明攻击的是老板的钱包。

那么问题来了。

既然那么危险,那为什么现在很多简单的小程序游戏服务端还是这么设计?

当然是因为游戏还没赚钱,需要快速上线,小作坊盈利要紧!更复杂更安全的游戏架构,意味着更高的开发成本,怎么说呢,别人都还没开始攻击老板钱包,你就先攻击起来了?想成为优秀人才被输送到社会吗?

只是开个玩笑,不好笑就当我没说过

有没有更好的方案?

有。

引入 Gateway 和 logingate

我们可以在客户端和 GameServer 之间再加一层网关服务器,也叫Gateway,于是服务架构就成了下面这样。通过多个 Gateway去连接 Gameserver。Gateway 内部几乎没什么逻辑,你可以认为它就是简单转发客户端数据到 Gameserver。

用户直连网关
用户直连网关

每个Gateway只服务部分用户,一旦它被攻击导致崩了,客户端发起重连,就能打到其他Gateway上,GameServer支持游戏角色掉线重连,从效果上来看,就像是你打着打着游戏进个电梯,从电梯出来后转转菊花,就又重新连上了。

gateway崩溃后不太影响用户
gateway崩溃后不太影响用户

但 Gateway 那么多,客户端第一次登录的时候,怎么知道自己该登录到哪个 Gateway 呢?因此我们还需要一个登录服务器,也就是Logingate,用于处理第一次登录的用户请求。用户登录时,Logingate 根据当前 Gateway 的人数分布情况,将用户分发到某个比较空闲的 Gateway 上,实现一定程度的负载均衡

加入logingate
加入logingate

引入 DBserver

用户在游戏里创建了角色,下线后过几天再打开,需要能接着玩。这就需要我们有个地方保存用户数据,比如mysql数据库。

Gameserver 中用户产生的数据都会写入到数据库中,但写数据库操作其实是很耗时的,因此一般不能放在玩家的主流程同步执行,只能异步操作。

当用户变多,异步写数据库的操作就会变多。Gameserver 是经常有版本迭代的,重启服务器很常见,这时候就需要等待这些写数据库操作完成后才能重启。

就挺不方便的。

怎么办呢?考虑到写 DB 的这一行为基本上不太会变化,完全可以从 Gameserver 单独拎出来,做成一个新的DBServer。于是架构就成了下面这样。

加入DBServer
加入DBServer

类似的还能拆的功能还有很多,比如商城服务器,语音服务器,聊天邮件服务器等。

这样Gameserver就可以更专注的去写游戏业务逻辑了。

一般 Gameserver 怎么扩展

玩家多次重复登录游戏时,用到的 Logingate, Gateway, DBServer 都可以是不同的

怎么理解上面这句话?

我不是每次登录都非得将登录请求打到同一个登录服务器 Logingate 上,只要任意一个 Logingate 能帮我验证下账号密码就好了。因此我们说这样的服务是无状态的,当请求的流量变大,无状态的服务感觉扛不住了,多复制几个同样的实例出来就好了,这就是所谓的水平扩展

但 Gameserver 却不同,对于多人在线游戏(mmorpg)来说,你这次登录的是 A 服务器,被里面的某个玩家砍下线了,你当然希望重新上线能回到那个玩家身边,再战三百回合。因此我们说 Gameserver 是有状态的,不能通过单纯的水平扩展来提升服务的同时在线人数。

那么有状态的服务器该怎么扩展呢?

对于常见的 mmorpg 类型游戏,比如大家刷网页最烦的一刀 999 的传奇类游戏,因为游戏玩法就是要多人同屏对战,对战时的伤害计算等逻辑全都是纯内存操作。技能释放时机,怪物或玩家角色死亡顺序都直接影响玩家最终的收益,所以它们需要有严格的先后顺序。这就导致了这类游戏,很多时候就是一个服务器大概几千人全都放在一个单线程里去执行游戏逻辑。

想要支撑更多的玩家同时在线的话,就得开新服,意思就是以前面提到的服务器架构(Gameserver, Gateway, DBserver, Logingate)为一组,平行扩展出非常多组这样的服务出来,服务器组与组之间完全隔离

所以你登录游戏的时候,经常能看到各种广东 1 区,上海 5 区。

分区分服
分区分服

当然,拆分服务区除了技术上的考虑外,还有游戏策划上的设计。比如广东 1 区和广东 2 区刚开服的时候火爆得很,每个区都会产生个top1的老板,后期玩家随着时间逐渐流失,就可以将两个区合并,重新刺激玩家进行竞争,为了保住 top1 的位置,老板们争相充钱。

小伎俩,但就是屡试不爽

这套组合拳下来,老板的青桔又能重新变回宝马

原神的 Gameserver 怎么扩展

但是「原神」明显不属于上面这一类拆分方式。

从登录界面可以看出「原神」并没有这种花里胡哨的分区方式。

目测是整个国内大陆同服

原神登录界面
原神登录界面

这是不是说,这么多玩家,全都挤在一个Gameserver里?

那当然不是,原神这么火爆,几个亿的在线,全都挤在一个Gameserver里的话,服务器根本扛不住。原神从玩法上就跟传奇这类游戏不同,传奇这类 mmorpg 的玩法就是千百人同时在线同屏 PK,玩家间的交互非常强。上次在这个地图被人砍下线了,再次上线还得回到原地对着砍你的仇家无能狂怒一下。

而原神大部分时候它就是个类似单机游戏的玩法,玩家就你自己一个,大部分时候几乎没什么玩家间的交互。不管你登录到哪个 Gameserver,它都能根据玩家角色上的进度数据调整剧情。因此大部分时候 Gameserver 就像是无状态的,那意味着 Gameserver 可以无限扩展

但它也不是纯单机,也有几个玩家组队刷怪的玩法,可以理解为一个小房间玩法,所以需要引入一些中心服务器(集群)去协调玩家之间的交互。

加入中心服务器后可无限扩展的游戏架构
加入中心服务器后可无限扩展的游戏架构

如果玩家之间发生交互,比如 A 玩家要进入 B 玩家的大世界,那就让中心服务器控制将 A 玩家跳转到 B 玩家所在的 Gameserver 中,将两个玩家放在一个线程里去执行逻辑。

而类似吃鸡和王者荣耀这种就是典型的房间类游戏,每一局游戏都会将限制人数n个人从游戏大厅转移到一个游戏房间里,实际上就是将n个玩家齐刷刷放到某个 Gameserver 中,通过单线程n个人的游戏逻辑跑起来。因此房间类服务器天然就是可以无限扩展的。

房间服务器
房间服务器

mmorpg 游戏服务器怎么拆分

上面提到,像传奇这类 mmorpg 会将所有玩家都挤到一个 Gameserver 中,因此单区单服同时在线能有几千人就差不多了。但如果我们确实想要提升单区单服的同时在线人数,有什么办法可以做拆分呢?

我们回过头来想想,所有人都挤在单个线程的话会有个很明显的问题,A 地图一千多个玩家在打世界 boss,卡的要死可以理解,但 B 地图新手村里没几个人,也会觉得卡卡的。

就不太合理,因此我们可以根据地图场景进行拆分,大部分游戏是可以根据地图和副本拆分成一小块一小块的,玩家之间一般也只会在单个地图里发生对战等强交互行为。

所以,我们可以将大地图拆成多个小地图,将 Gameserver 也分成多个,每个 Gameserver 负责一部分小地图,这样地图拆的越小,单个 Gameserver 负责的人就越少,于是乎游戏整体承载的人数就变多了。

是不是很妙!!

同样,切分地图的方法其实也非常适合「原神」这种超大世界地图的游戏。

一个子地图对应一个服务器
一个子地图对应一个服务器

总结

  • 单机游戏有被作弊的风险,因此可以考虑将核心数据放到游戏服务器中。
  • 让客户端直连Gameserver,可能会导致暴露核心服务器IP,有被攻击风险,因此需要在客户端和Gameserver之间加个GatewayLogingate,同时还能将数据存储相关的功能拆出去变成DBServer,同样还能拆出聊天服务器邮件服务器等,让Gameserver更专注于业务逻辑。
  • Gameserver本身是个超级单体,支撑的玩家数量有限,想要支撑更多玩家人数的话,就需要对 Gameserver 进行扩展。根据游戏的不同,我们会有不同的策略,比如传奇这类mmorpg可以考虑多开几个区服,或者根据地图去拆分。王者农药和吃鸡是房间类游戏,天然支持无限水平扩展,「原神」大部分时候更像单机游戏,因此也很适合水平扩展。

最后

上面提到了一个将大地图拆成小地图的方式去拆分游戏服务器的策略。这对于地图与地图之间交互较弱的游戏非常适合,但对于一些无缝超大地图的游戏,玩家是要能看到对面地图的玩家和怪物的,如果对地图进行拆分,这种情况该怎么处理呢?