9月23日,首届“梦想·匠心”腾讯游戏开发者大会于深圳举行,在技术分论坛上,盛大游戏《龙之谷》手游技术总监李阳分享了龙之谷的服务器设计。作为《龙之谷》的手游技术负责人,李阳从事多年游戏后端开发,参与多款上线项目研发。李阳于本次论坛上分享《龙之谷》手游服务器在架构设计、灾备处理、性能优化、压力测试等方面遇到的一些问题和经验总结。
以下内容为分享实录:
李阳:谢谢大家!我大概介绍一下我们项目的情况。
我们项目签约腾讯之前处在Demo阶段,团队更关注核心战斗玩法,当时服务器的架构和辅助系统都是最小实现的模型,实现很简单。2016年上半年签约腾讯之后,我们集体入驻到深圳,项目组花了9个月时间,把整个服务器几乎重写了一遍,那时候时间节点已经定好了,我们更多的是调整架构,实现功能,保证整个服务器的稳定性、承载性能达到要求。腾讯对项目上线是有标准的,有三轮评审,评审不通过不能上线。我今天针对这中间遇到的问题和大家做交流分享。
设计标准
腾讯对项目上线有一系列标准,在此简要列举几点:1、架构设计要合理,支持弹性动态扩容能力,高可用容灾能力。2、服务器性能要满足要求。3、符合DB规范。4、符合运维规范。
设计架构要求:1、弹性动态扩展能力,逻辑层要求支持线上动态扩容,并且要求对用户无影响。2、高可用容灾能力,核心模块和关键路径不允许有单点,不允许无法扩展。3、要求对同时大并发访问有防雪崩机制,比如排队、访问频率控制等,防止过载。4、要求非关键核心模块故障不影响玩家主逻辑服务,可提供有损服务。5、不允许数据变更未及时入库,导致可能发生10分钟以上的回档。
DB规范:1、按需Cache,存储层实现分库分表平行扩展或接入TSpider集群。2、使用域名+Port的配置方式访问存储层。3、存储层访问必须支持故障或者超时后的重连机制。
运维规范:1、接入腾讯TGW。2、客户端需采用域名+VIP的配置方式访问服务端。3、日志文件支持按小时或大小滚动。4、程序用到的系统参数不能hardcode到代码中,必须以配置项形式写在配置文件中。5、应用程序必须支持通过工具实现动态加载相关的配置文件,而无需中断服务。6、苹果审核服和正式服的访问切换必须通过服务端控制实现。
服务器架构
单服架构:龙之谷是分区分服的,单服架构如图,稍后会详细介绍单服内模块。
单服内模块:
1.网关服务器:(GateServer)负责保持客户端的连接,负责通信解密,数据压缩,维护客户端连接的保活,多开负载均衡和容灾。
2.游戏服务器:(GameServer)负责场景内的所有游戏逻辑 ,多开负载均衡和容灾。
3.全局服务器:(MasterServer)负责全局游戏逻辑,公会,好友,家园,排行榜等,单点,宕机拉起后根据DB中的数据和各个GS上报的信息做恢复。
4.控制服务器:(ControlServer)负责登录,在线数据维护,切换场景控制,实现多个GS间的负载均衡,单点,宕机拉起后根据共享内存中数据恢复。
5.数据库服务器:(DBServer)负责存取数据,维护缓存,单点。
6.语音服务器:(AudioServer)负责在内存中缓存语音文件,单点。
单大区架构图:这是单大区内完整的结构图,由多个小服 加上跨服集群,再加上大区内全局服务器构成。
单大区内的模块:
1.分4个大区: ios-qq ios-wx android-qq android-wx
2.登录服务器:(LoginServer)负责验证玩家登录请求,维护区服列表,多开负载均衡和容灾 。
3.同玩好友关系服务器:(CenterServer)维护游戏内同玩好友信息,多开负载均衡和容灾。
4.跨服匹配服务器:(WorldServer), 负责跨服匹配,开启跨服局游戏,主备容灾。
5.路由节点集群: (RouterServer),小服与跨服GameServer集群之间的通信中转节点,多开负载均衡和容灾。
6.跨服GameServer集群:承载所有跨服PVE, PVP局,多开负载均衡和容灾。
跨服匹配服务器,分主服务器和备用服务器。主的是现在只有一个生效,跨服的GS同时连跨服匹配服务器,主从都连。
跨服模块:
1.跨服玩法由跨服GameServer集群承载,一个机器上开N个进程,一个大区开几台机器。
2.增加路由节点,在小服的各进程和跨服GameServer进程之间转发数据,避免小服和跨服GameServer之间的连接关系膨胀,或跨服时需要新建一条TCP连接。
3.路由节点组成集群,所有路由节点都向跨服匹配服务器汇报自己监听小服连接的IP、Port,当各小服注册到跨服匹配服务器时,跨服匹配服务器会为该小服分配一个(或多个)压力较小的节点,小服去连接。
4.跨服匹配服务器做主备容灾,正常情况下主进程执行所有逻辑,主进程挂掉后被监测到时,监控程序更新DB中的主ServerID,备进程会定期读取该ServerID,一旦读取到主ServerID是自己,随即向所有小服,路由节点集群,跨服GameServer集群广播自己的身份,备进程成为新的主服务器,继续提供服务。
全区全服模块:
1.IDIP服务器:负责接受平台的IDIP接口请求,转发到各小服/登陆服务器做处理,处理后返回结果,多开进行负载均衡和容灾。
2.Version服务器:负责维护当前IOS/Android的版本号,客户端根据这里获取的版本号来决定是否要更新补丁包,多开进行负载均衡和容灾。
3.电台服务器:(FmServer),负责FM电台逻辑,微信平台共用一组,QQ平台共用一组,主备容灾。
单服内容灾:
1.GateServer宕机, ControlServer会将该Gate上的所有玩家踢下线,所有连接到这个Gate上的玩家都将断开连接,无法通过断线重连机制重新进入游戏,只能重新登录进入,进程被重新拉起后可恢复服务。
2.GameServer宕机,ControlServer会将该GS上的所有玩家踢下线,玩家被踢回登录界面,重新登录后可进入其他的GS继续游戏,挂掉的进程被重新拉起后恢复服务。
3.DBServer宕机,DBServer负责存取玩家的数据,挂掉后玩家无法登陆,GS上保存数据的协议会超时,但超时后会重试,被重新拉起后恢复服务。
4.MasterServe宕机,好友,聊天,语音,公会,邮件,组队等服务不可用。但不影响在所有在线玩家的战斗,移动,切场景等核心游戏体验。MS重新被拉起后恢复服务。
5.ControlServer宕机,无法登陆,无法切场景,但重新被拉起后恢复服务,相关数据(在线玩家数据)被存储在共享内存中,重新拉起后根据这些数据重新恢复现场,恢复服务。
6.AudioServer宕机,无法发送语音,无法提取语音。
大区内容灾:
1.单服内的模块支持进程级别的容灾,大区内的全局模块支持机器级别的容灾。
2.登陆服务器, IDIP服务器,版本号服务器多机器部署容灾,由TGW轮询做负载均衡。
3.同玩好友服务器,多机器部署容灾。
4.跨服匹配服务器,电台服务器做主备部署,主服务器出现故障时,备用服务器自动接入系统,成为新的主服务器继续提供服务。
体系结构
我们在服务器的体系结构上进行分层设计:网络层提供通用的二进制传输,QQ转让平台中间是框架层,提供RPC、Protocol、定时器、网络连接封装,RPC封装,由工具生成RPC代码。
网络层:
1、跨平台的网络模型封装,对外提供统一的抽象接口,内部Windows下封装IOCP,Linux下封装Epoll。
2、网络IO在单独的线程中执行,不影响主线程逻辑,通过无锁的环形队列与逻辑线程交换数据。
3、Windows下开发调试,Linux下部署运行,提高开发效率。
框架层:
1、主要提供基于时间轮的定时器;
2、网络连接的封装,自动重连,心跳保活;
3、日志支持;
4、RPC,封装通信请求、处理;
5、协议的注册、处理分发;
6、Utility。
应用层:
1、场景管理,静态场景+动态场景。龙之谷有一个主城,是静态管理,副本是动态创建,根据负载均衡副到各个不同的服务器上。
2、视野管理:主城9宫格+战斗场景全同步+特殊场景分线。主城是九宫格视野管理,战斗场景全同步,我们战斗场景人数不多,全场景同步,特殊场景分线,公会大厅,公会中有很多人,这些人都是按照静态分线机制,N个人之间可见。
3、AI是基于行为树实现的。
4、战斗:服务器结算所有的逻辑,做状态同步。
5、协议:RPC用工具定义,自动生成代码6、各逻辑系统。
性能优化
1.运用性能分析工具(Gprof, Perf)找出瓶颈模块,热点函数。
2.对热点模块进行优化:优化设计,减少遍历,空间换时间,运算降频。
3.对热点函数进行优化:降低时间复杂度,尽量到O(1),空间换时间,能算一次的只算一次,后续直接使用结果。
4.注意一些依赖库的坑,比如GCC-4.4.7版本stl中list的size()是遍历计算大小的。
5.统计服务器的各种压力参数:流量,协议发送/处理频率,定时器数量等,定期输出到文件中。
6.架构上支持进程多开,分摊压力。
性能优化-压测
1.前期删档测试期间记录下高峰期服务器所有协议的处理次数,处理时间,得到重点协议的接收频率,处理时间消耗等数据,有针对性的优化。
2.根据测试得出的协议接收频率模型,腾讯压测部门写机器人工具,直接发协议到服务器,模拟真实玩家,对重点流程(登陆,切换场景,战斗,大规模的活动等)进行压力测试,测试响应速度,重点事务tps,找出瓶颈。
3.使用机器人进行综合测试,模拟大规模玩家的行为,不停上下线,不停切场景,打副本等,持续跑几天,观察CPU负载,内存增长等指标。
4.压测下来,单服最高承载8000人,出于保守考虑,最高允许的同时在线人数设置为7000,到了7000就开始排队。
《龙之谷》的经验教训:
1.做好容灾,服务器各进程要做到宕机拉起后可重新恢复服务,极端情况是可能出现的,龙之谷经历过:
<1>. 服务器硬盘写满,所有进程一起崩溃,要防止重要数据丢档。
<2>. 虚拟机母机硬件故障,整个机器挂掉,要防止重要数据丢档。
<3>. 内网波动,服务器之间的连接中断一段时间后又恢复,要有自动重连机制。
<4>. 网络波动导致服务器进程和 TSpider之间的连接断开,要能够处理这种情况,定时尝试重连TSpider,未写入成功的数据要缓存起来,待连接OK后继续写入,若重试达到一定次数,则禁止玩家登陆,等候运维介入。
2.控制好内存分配,多用内存池,对象池,能事先分配好的事先分配好,各系统的内存申请、释放尽量统一控制,禁止随意分配。内存中的Cache要控制上限,避免无限膨胀。
3.流量、协议发送/接收频率,场景数量,各系统性能统计数据等定时记录,以备查问题时用。
4.关键流程和大规模活动玩法 要有频率控制,防止压力不可控。我们做了很多全服玩法,中午12点30开世界Boss,打的人很多,晚上也有公会Boss,还有大规模的跨服活动玩法等,这类玩法的关键流程要有频率控制,防止压力不可控。
5.重要数据实时落地,防止意外情况重要数据丢失,对于非重要的数据丢失给玩家补偿也可以接受。
6.重要流程(支付等)的日志要非常详尽,有问题方便排查。
7.数据库表格要合理设计,重要数据单独成列,避免更新时被其他数据影响。龙之谷的设计是每个系统的数据自成一列,做成Blob数据,如果某些重要的数据和其他非重要的数据放在一列存储,非重要数据的更新如果有BUG,可能会影响重要数据,得不偿失。
8.测试用具要完善,单元测试,压力测试,模拟客户端,压测机器人等。
9.负载均衡要多考虑,避免各节点之间压力不均,要均摊到整个集群。我们设计跨服路由集群的时候也考虑过负载均衡,但后来发现某些情况下,整个集群内部的各个节点的压力不均衡,有的节点压力很大,有的很空闲,我们后来优化了一下,最好在最初设计时多考虑权衡,做到把压力均衡到整个集群。
10.引入脚本,做到部分逻辑可热更,或通过脚本修复某些错误。如果代码全部是二进制,出现某些问题需要重启服务器,有脚本会更加灵活一些。
11.逻辑系统做功能开关,出问题后可动态关闭特定功能,防止问题蔓延。龙之谷所有系统都做了动态开关,根据系统ID可以关闭或打开一个系统,如果某个系统出现问题,就动态的关掉。
12.重要数据异常,严重错误要监控记录。比如玩家数据膨胀,最开始创建角色的时候玩家数据很小,玩家玩的过程中不断增大,每个系统数据的大小都会被监控,发现异常就会警告,我们再去排查。
13.所有大规模的全服,跨服玩法上线前都要做压力测试,尽可能的模拟真实情况,避免上线才暴露问题。
14.数据做集中式存储,对后期合服会很友好。尽量把全区全服的数据存储在一起,这样后期合服就不需要合数据。
15.对日志做关键字监控,便于知晓问题的发生。通过脚本进行监控,发现问题实时上报。
谢谢大家!
这是龙之谷上线过程遇到的问题,看看大家有没有什么问题。