机缘巧合的机会,我有幸可以从头开始设计一个游戏的服务器。中间遇到不少欢声笑语和悲伤泪水,这里分享一下。
html
我以前所在项目组的游戏服务器架构以下图:程序员
这款游戏是一款MMO的端游,GateWay网关的任务是接受客户端的链接,而后经过分发策略,把玩家丢进GameSvr上去,以后玩家的全部请求都直接发给GameSvr,由GameSvr处理了。固然这里的分发策略跟通常的web服务器是不一样的,web服务器通常会作成无状态的服务器,也就是对于客户端来讲请求到达哪个服务器都没有关系,都可以被处理,可是游戏服务器大多都是有状态的,web的通常分发策略是作一个负载均衡,只要保证服务器的总体压力没问题就已是一个好的架构了,可是游戏服务器因为存在状态须要维护,因此一般都没办法作简单的负载均衡。举个例子,因为咱们是MMO游戏,因此在服务器逻辑上存在地图的概念,比如魔兽世界的游戏里不一样的玩家出生的地图是不同的,并且你从一张地图下线后,下次上线的地点通常来讲都会是你上次下线的地点,而根据游戏历程和策划想要营造的游戏效果来看,游戏里不一样地图之间的压力一般来讲是不同的,好比主城的玩家就一般比一张偏远的野外地图的玩家要多,而游戏服务器的压力一般都出如今玩家汇集的时候,例如大量IO的压力。因此从设计人员的角度来看,通常会把不一样的地图分配到单独的进程中去,因此GameSvr一般会按照地图来进行划分,那么GameSvr进程天生就具有了本身的状态,由于两个GameSvr进程已经就不同了,玩家是在哪一个地图,就只能把玩家丢到哪一个GameSvr上去。固然,这只是咱们采起的一种作法,其余人还有不一样的作法,不过大体是相似的。web
我曾仔细考量过上面那个游戏单个GameSvr的承载能力,一般在1000人左右就已经开始吃紧了,这点也比较好理解,根据这个服务器的架构,几乎全部的玩家逻辑都在同一个进程内被处理,只有一些公共的逻辑被分配给Relay进程处理,这里的Relay进程承担两个做用,一个是作不一样的GameSvr间的通讯转发,另外一个是它自身也会维护游戏里的公共逻辑,什么叫公共逻辑呢?好比聊天、帮会、排行榜等等,这些不跟地图相关的游戏逻辑,又被多个GameSvr共享的部分就被抽离到Relay进程上,其实这里有两种作法,一种是让每一个GameSvr进程本身去维护这份公共逻辑,但显然这会浪费GameSvr进程仅剩的很少的压力负载。可是把逻辑放入Relay进程也带来一些问题,好比在游戏研发中会发现,愈来愈多的逻辑须要放进Relay进程,致使Relay进程愈来愈庞大,好比咱们游戏中的帮会数据和全局定时器的维护就由Relay进程来维护的,可是程序员在写功能的时候常常就直接一个RPC调用来使用Relay的功能,由于这很方便,并且从现有架构的设计来看也是合理的,咱们当时的relay的rpc接口设计的是同步的接口,因而当rpc激增的时候,整个游戏逻辑就变卡了。sql
Relay的庞大致使两个问题,一个是单点故障的几率变高了,另外一个就是Relay的性能低下会致使整个游戏服务器变卡。咱们的服务器有段时间在天天晚上10点左右就开始卡,只要涉及到公共逻辑的部分就几乎没法使用,例如没法聊天、没法进行帮会操做等等,最后排查问题的结果就发现是游戏中的某些功能把逻辑写在Relay上,到10点进行了一个十分复杂的计算,致使Relay阻塞在那里,而没法响应GameSvr发来的请求。我是在游戏的开发末期加入这个项目的,当时Relay的体积仍是比较小的,你们都刻意的去尽可能避免游戏逻辑放在Relay上,可是当游戏真正进入运营期以后,deadline的限定,你们不得不把逻辑放Relay上,由于这是最快的实现方式。我眼看着Relay一步步的胖起来,在我离开这个项目的时候,Relay上的代码已经满目苍夷了。数据库
再来谈谈这个架构的其余部分,好比Bishop是用来进行咱们游戏内交易数据的统计和验证的模块,主要是和公司的交易系统进行对接,AccoutSvr的做用是处理游戏内帐户的相关操做,好比创建帐户,新建角色等等,它的主要做用实际上是为了保证游戏数据的惟一性,例如角色名的惟1、宠物名的惟一等等。接着是Mysql的部分,云风曾经在博客里说过游戏服务器中数据库的做用应该只是一种备选方案,http://blog.codingnow.com/2014/03/mmzb_db.html 也就是说若是存在理想的游戏服务器,永不宕机,永不维护,那么实际上数据库是不须要的部分,数据库承担的是一个游戏恢复和容错的机制,我很赞同云风的这种说法,而一旦不须要去考虑数据库的部分,那么游戏设计其实减掉了不少容错的作法。关于数据库的部分我后面还会谈到。后端
而后是这个架构的扩展性,对于游戏服务器来讲,扩展一般有三种,一种是开新区、另外一种是合服、最后才是因为服务器压力顶不住而扩展逻辑进程。上面咱们这个端游的服务器模块从设计上就不去考虑开新服的机制,单套服务器架构基本只支持一个区,因此在开新区的时候,作法是部署同一套服务器架构,利用客户端更新服务器的地址来实现不一样服的架构;合服的状况要更加复杂,由于在设计上没有考虑多服的设计,因此不一样服之间的数据能够说是绝不相关,那么在合并服务器的时候必然存在一些冲突数据须要处理,不光是一些玩家数据,甚至还有一些游戏逻辑数据,例如我以前作过一个功能,是实现一个全服的玩家联赛,最后每一个区会产生一个冠军,而后冠军的雕像会被放置到游戏中的主城中展现一个月,可是后来有两个区合并了,那么到底展现谁的雕像,由于不管你展现哪个区的,玩家都会不满,固然,这种事情通常须要策划去考虑而后解决,可是从程序上来讲,确实存在这种须要特别处理的地方。在合服的时候,另外一个问题是以前保证的角色名不重复是利用的各自区的accsvr,一般accsvr保证唯一性的作法是使用同一个数据库来保存须要排重的信息,可是对于不一样的网络运营商,这个数据库极可能没办法统一块儿来,这样一来,当两个处于不一样网络中的区须要合并的时候就出现了难以解决的问题,好比咱们有一次把电信和网通的两个区进行合并,原本这两个区各自确实可以保证角色名惟一,可是当跨运营商合并的时候却会发现冲突。服务器
而后是服务器压力,不知道是因为当年设计这个架构的人没有考虑到仍是当时不存在这样的问题,因此服务器横向扩展的能力是十分弱的,对于新开的副本,relay进程会根据gamesvr的压力来选择把新副本开在哪一个gamesvr进程上,但对于常驻地图,倒是写死在配置表里,绑定固定的gamesvr,而relay又没有提供动态添加gamesvr的功能,这致使了若是在服务器运行期间出现了单个服务器进程压力,除了重启服务器,几乎没有任何办法。这个在运营期暴露的很明显,因为GameSvr按照地图进行划分,当开新区的时候,玩家大量涌入同一张地图,致使单个GameSvr压力出现峰值,但这个时候却没办法动态的扩展进程进行分压,而致使服务器宕掉。在后期的运营中这样的状况家常便饭。网络
后来我还听到过不少关于页游的架构,页游服务器总体上跟端游思路是相似的,因为客户端的一般通讯方式再也不像端游那样采用原生tcp链接,而是使用http等短链接的协议,因此在客户端链接的部分设计上更加灵活,并且大部分页游的游戏逻辑没有端游那么复杂, 在客户端的表现力有限的状况下,基本上总体的游戏服务器设计要更加精简,因此我看到一般服务器后端会更加偏重于如何进行横向扩展。上面我提到的架构扩展性差的问题,对比页游的滚服方式,体现的最明显了。架构
这就是关于我上一个游戏服务器的样子了,下一篇开始我本身的服务器设计。负载均衡