本文由腾讯GAD平台授权发布
天涯明月刀在测试期间,由于放号量大,玩家热情高,服务器一直承担很大的压力,生怕由于宕机不可恢复等原因造成玩家流失或是口碑下降。好在通过一系列预定方案的实施和不断优化,虽然开服期间也出现过各种宕机问题,但是也可以说很好地顶住了这段时间的压力。这里就抛砖引玉地总结一些我们用到或是想到的MMOG服务器容灾方法。
天刀的服务器采用了典型的MMOG后台架构:
上图进程按照部署结构,可以分为三个层级:
CLUSTER级:提供全局服务,如版本更新tcus、目录树tdir、账号信息tacc等。CLUSER级服务基本采用公司架构组提供的通用组件。
IDC级:主要提供跨world的账号服务器,如激活封停服务accountsvr、IDC级账号服务accountsvr等。
WORLD级:每个世界的主要逻辑进程,按照功能又可分为接入服务world conn, 全局服务world global, 场景服务 world scene三个大类。其中world conn是接入服务进程的集群,world global提供全局的账号验证,数据存储和负载均衡等服务,world scene是场景逻辑进程的集群。
在讨论容灾的具体方案之前,我们先明确一些概念:
有状态和无状态进程:无状态进程泛指任何时刻无任何需落地数据的进程,也就是宕机后只需要重启即可重新提供服务,常见于应答式服务,如tdir/tcus/torm等。有状态进程相反,宕机后可能会造成系统整体数据和状态的不一致,需要一定机制进行状态的恢复,比如world/scene等。
冷备和热备:指防止故障造成服务暂停的两种方式,热备一般指使用2倍的资源提供服务(平时负担只有50%),主机宕机后,备机接管所有请求;冷备指平时不工作,空跑,事故发生后接管请求。
结合以上点来看,无状态进程的容灾比较简单,一般只要做好冷备或者热备即可, 比如我们整个CLUSTER级的服务,采用了公司的通用组件,均提供主备从架构,并支持异地部署,很好地解决了容灾问题。因此以下不提,下面主要总结一下MMOG IDC级和WORLD级相关服务的容灾。
和互联网业务一样,MMOG的容灾同样可以分为三个层面:
1.接入层容灾:接入服务器宕机是否对登陆玩家是否能做到无感知,对在线玩家是否能直接在游戏内进行断线重连,而不必重启发起qq登陆。
2.逻辑层容灾:任一逻辑进程宕机能否对玩家做到透明,能否自动拉起,是否支持动态扩展。
3.数据层容灾:在灾难发生的情况下,是否能保证玩家数据的一致性,是否把玩家数据的回档控制在最小范围内。
接入层容灾策略:
world conn service:
前端采用公司TGW解决方案,既解决了多路接入的问题,其本身也提供了OSPF集群容灾,保障集群中任一服务器出现故障仍能保证包的正确转发。
后端采用tconnd集群,保障任一tconnd宕机后,客户端仍可以选择其他tconnd接入:
我们采用自定义的authsvr做全局的激活验证、心跳保持和负载均衡。玩家登录时首先从authsvr通过验证并取得可用的tconnd的地址,然后进行连接,这样就可以跳过发生故障的tconnd;如果是在线玩家所在tconnd发生故障,由于逻辑进程玩家并未下线,客户端也可以选择新的tconnd重新发起连接(前提是客户端没有crash)。这样就解决了单个游戏tconnd故障的问题。
但此时authsvr本身成为一个单点,并且是一个有状态的单点进程(保存了各个tconnd的负载信息),但是注意到只要world以较快频率(比如每秒1次)的速度上报,authsvr重新拉起后就可以马上恢复到正常工作状态。
逻辑层容灾:
首先说明一下,逻辑层所有有状态的进程我们在设计之初就把数据全部存放在共享内存之中,这样即便发生宕机故障,仍然可以以resume方式启动,并重新attach内存即可继续提供服务。因此下面讨论的均为resume失败需要重启的情况。
IDC service:
IDC层有负责做激活和禁号验证的accountsvr和负责登记IDC所有在线玩家账号信息(通过world上报)的loginsvr。设计之初,我们尽量往无状态的方向去靠。最后accountsvr成为无状态服务,宕机后只需重启即可;而loginsvr目前仍然是有状态的,如果自身发生宕机需要向IDC内所有world请求一次全量信息同步。在同步未完成期间(几s之内),可能造成该IDC上一些玩家激活验证的失败。
逻辑上一种更简单的做法是将玩家的在线账号信息做一个持久化保存,但是会带来每一次部署和维护上的冗余操作(清理数据库),最终未被采用。
world global service:
这一块包含一些无状态的组件进程(torm/tmail),以及登陆验证相关的authsvr进程,最后就是负责核心功能包括玩家登陆、负载均衡调度、全局信息存储的world进程。
world作为一个有状态的单点,如果发生宕机拉起失败,会是灾难性的,这意味着整个游戏世界停止服务,并且scene上所有玩家的数据会产生丢失。
因此首先world上的功能需要设计尽量简单,所有的旁路功能,如聊天/拍卖行/帮派等等尽量都独立为旁路进程,降低world上逻辑的复杂度,减小宕机的可能性。
另外我们在scene和world之间维护一个心跳协议,如果发现world发生宕机拉起失败,那么scene上会主动发起踢人,并把玩家数据dump到本地,等待下次服务器重启的时候再把dump文件中的数据进行存盘,把玩家的数据丢失可能降到最低。
world scene service:
所有的scene进程其实分摊承载了游戏内所有的地图和玩家,玩家的数据变化一般都是在scene上产生的,所以测试期间scene发生宕机的概率在所有逻辑进程中是最高的。对于scene宕机拉起失败的情况,我们有几种措施:
1.world通过心跳协议发现scene宕机后,立即从自己的管理逻辑中剔除该scene上所有玩家,避免玩家状态不一致引起的登陆失败。
2.由world通过注册协议决定地图在各个scene上的承载,某个scene宕机后world根据情况可在其他scene上重新寻找位置创建地图。
3.宕机的Scene可以重新发起注册,由world逻辑决定后续地图的承载。
注意scene宕机是会引起部分玩家掉线和数据丢失的,关于后者在数据层容灾中还会继续谈到。
数据层容灾:
数据层容灾有两个方面:
一方面是持久层的容灾,比如采用了Mysql可以使用Data Replication机制。另外公司的游戏云存储平台GCS对Mysql做了进一步的封装控制,业务可以使用domain+port来访问Mysql,实现平滑迁移和故障切换。
另一方面指逻辑服务故障引起的数据丢失(或称回档)。由于在测试阶段宕机无法100%避免,我们有一系列措施尽力减少由于宕机故障引起的用户回档损失:
1.每个玩家每3分钟存盘一次。
2.关键操作(拾取高级装备、升级、交易等)实时存盘。
3.world宕机,scene把玩家数据dump到本地,等待重启后存盘。
以上方案结合基本可以避免常见故障下的玩家重要数据丢失。