默认
打赏 发表评论 6
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
《王者荣耀》2亿用户量的背后:产品定位、技术架构、网络方案等
微信扫一扫关注!

1、前言


作为一个国民级手游,《王者荣耀》公测几年来的表现一直很强势。在耀眼的成绩背后,到底是什么样的技术支撑了这个用户量破2亿的游戏?王者上线前后在技术上有哪些变化、又是如何处理网络问题的?

《王者荣耀》能够成为如今国内最成功的手游,其后方成熟的技术团队可以说是功不可没。这个曾经在端游时代主导搭建RTS游戏《霸三国》框架的技术团队,在转型做MOBA手游《王者荣耀》后为游戏提供了巨大的支持。但这个过程也并非一帆风顺。在首届由腾讯游戏学院主办的以“梦想•匠心”为主题的腾讯游戏开发者大会上,腾讯互动娱乐《王者荣耀》技术总监孙勋在技术专场中,对这款游戏进行了一次技术复盘,对《王者荣耀》的后台技术做了详细分享,为听众嘉宾就从技术层面上讲解了游戏在引擎、整体网络架构与网络同步方案上的尝试与转变。

以下是分次分享的内容。

2、分享者


1.jpeg
孙勋:一名后台开发程序员。2005年加入腾讯,最开始不是做游戏,2007年前一直做拍拍网,2007年加入成都卧龙工作室,也就是现在的天美L1工作室。之前参与过《QQ三国》、《封神记》、《霸三国OL》,到后来的《王者荣耀》,现在是这款游戏的技术总监。

3、内容概述


今天分几部分和大家介绍王者后台开发过程中的一些内容和思考:包括王者整个背景的介绍,后端的架构,上线之后做了什么样的调整,还有网络同步方案,反作弊方案等。

目前游戏的服务器架构主要由“游戏大厅”和“PvP”2个部分组成,而在不断探索中,其后来又在架构中加入了Proxy中转服务器,也正是这个服务器的加入为《王者荣耀》解决了后来“安卓、iOS”同服等一系列出现的问题。此外,他还介绍了《王者荣耀》在网络协议以及同步方案上的一些尝试,并一一复盘了这些尝试的优劣势,并解答了为什么,最终游戏会放弃TCP协议(传输控制协议)与曾经在《霸三国》中所使用的CLIENT-SERVER结构(C/S结构),并且转而使用了UDP协议(用户数据报协议)与帧同步同步方案

我能透露的数据是,现在王者后端的机器大概是4600多台机器,我们的容量也有一定的扩展,进程数目是4万多个。

4、《王者荣耀》的产品背景:原本是RTS端游《霸三国OL》


2012年,我们当时做端游游戏,是王者的前身。最开始是偏向RTS的游戏,后来我们把它改成端游的MOBA,后来做手机上面的MOBA即王者。从2012年开始做RTS游戏到2013年,从多控制单位的RTS游戏 变成MOBA游戏,到2014年启动王者的预研,再到2015年2月份我们把很多的人力(大概100多号人)投入做王者开发,时间比较短。6月份的时候,大概4个月开发时间,我们就开启了王者的对外技术测试,8月份开始做不删档测试,听了主论坛就知道,那时候我们叫英雄战迹。再到后来《王者荣耀》10月开始不限号。时间很短,任务也很重,压力也很大,主要全量的开发时间就是3-4月,这也决定我们当初技术选型方面的考虑。

做王者之前,我们做的霸三国,最开始是偏RTS的游戏,后来我们改成了PC上的MOBA游戏。

前面我们提到《霸三国OL》的RTS游戏,其实大家可以可以了解一下这个完成度已经是比较高了,但玩起来的话还是有一些问题,这个方案就被放弃了。

《霸三国》的玩法是,玩家可以在战前通过排兵布阵构成自己局内的策略,通过控制多个单位,技能释放、兵种特性的释放形成对抗。我们最开始做《霸三国》的时候客户端引擎是unreal,但在做王者的时候引擎用unity,3-4个月的时间,本身从代码层面没有任何东西是搬过来用的,全部都需要重写。

5、《霸三国OL》带来的技术启示


做端游《霸三国OL》的这段经历,给我们做王者带来很多相应的启示,比如策划、程序及整个团队对MOBA的理解。另外当时在做端游RTS、MOBA的时候,我们采用了CS(Client-Sever)的模式,但其实在过程中有借鉴类似帧同步的概念:例如在断线重回对视野的处理这块。

传统的做法是重回时会发当前的镜像和后续的其他下行通知信息,但传统做法会有一个问题,如果新增其他的场景内模块的时候,根据场景内包含的当前的各种物件、所在状态的各种各样信息,都需要把这些东西打包发下去,在后续开发、维护的时候挺麻烦的。我们的做法,就是类似帧同步的理念,把服务器下发的所有序列包做缓存和按顺序重发,让客户端做快进的表现,它的概念和帧同步其实是比较类似的。还有一点,就是预留设计弹性,最开始RTS每个玩家最多可以操作5-8个进行对抗,到后来改成MOBA游戏,只能操作一个英雄,加入各种各样的场景,我们本身的技术框架没有进行颠覆性的大改。

6、《王者荣耀》的整体技术架构


王者目前后台的整体架构的设计是源自产品的需求,大家玩过王者的就知道,PVP的对抗是不分区服的,你登录微信1区可以和微信2区一起PVP,甚至ios平台可以和Android平台的人一起玩PVP,但是同时又保留了分区的概念,比如战队、排行榜是基于区的概念,区在王者里面就是编号,是打在玩家新建角色上的Logo。

我们最开始做架构实现的时候,服务器当时做得比较简单,从原型开始只是保留了大厅和PVP服务器这两块,它本身是分开的。PVP服务器的使用类似CGI调用,可以分配资源使用,用完之后回收,不负责其他的东西,需要的东西从大厅拿,用了之后回给大厅,让大厅回写DB。包括大厅和PVP之间做直联,后来把直联改成中间的转发,在王者里面我们叫Proxy,相当于代理服务器,屏蔽本身后端很多进程分布的细节。因为王者本身的机器、进程很多,还有不同的路由规则,比如某些排行榜或者战队根据逻辑区的编号来确定有哪台机器或者多台机器进行处理,有些消息采用随机转发或者多发广播,都是由Proxy负责。

之后又加入了房间服务器,它负责的是王者里面匹配、排位相关的功能,怎么样把实力比较接近的人糅合到一块儿玩,是由房间匹配服务器来做相应的负责的。也会有战队和其他的服务器。最后我们在上面加入了一个Adapter,作用是用本身已经部署的大区资源实现跨平台匹配的功能。王者的后端架构,除了战队这样的服务器之外,所有其他的模块都可以在线扩容,或者在发现在线下降的故障时,从整个架构里自动屏蔽掉。因为路由方式会限定比如一区、二区、三区到这台机器处理,如果故障,影响的只是某几个逻辑区玩家请求的处理,降低故障影响范围。

王者目前的机器数量,可能每周都会发现有机器坏掉,至少有一台机器宕掉,在架构里面保证模块故障自动屏蔽和在线扩容,是非常重要的事情。整体结构比较像MMO的三层结构,MMO在腾讯有比较典型的三层级别的结构。大厅服务器会根据玩家的所在区登录具体区的大厅服务器。单个大厅进程可以承载2万人,单个PVP可以承载1.2万,小区登录微信一区还是二区就是角色Logo打在他身上。其实王者就是全区全服的结构,如果剥离了战队,它就是一个全区全服结构。不管是大厅还是PVP服务器,其实每个都是按组的概念。我们可以在线扩容,如果组里面某个宕掉,不会有什么太大的影响。

2.jpg

王者现在外网有四个大区,比如Android手Q、Android微信、iOS手Q、iOS微信,还有抢先服。我们会用程序开关的方式,在大版本发布之前,优先更新抢先服,这时候它不能和正式服玩家匹配在一起,因为他们的版本不一致。当全服发布之后,它的版本更新一致之后,我们会打开开关,抢先服的玩家可以和正式服的玩家一起进行PVP的匹配。

除此之外,我们还有专门的体验服,是给策划验证相关设计的,体验服保留可能删档的操作,但在外部正式环境这是绝对不允许的。

另外,以前的传统手游偏单机,就会做很多协议兼容,客户端版本没有更新可以玩。但是王者里的主要玩法是PVP,同时结合实现方式,不同版本的玩家不能匹配一起,所以我们没有做任何的协议兼容,因为这样做起来也很麻烦

3.jpg

7、上线后的调整


上线后,王者本身的后台架构,整个结构没有做太大的改动,因为我们做端游的时候,对这套结构比较清楚,我们知道哪个地方可能有什么样的问题,所以整个结构一直比较稳定。

但是我们做了相应的微调,做得最多的是网络本身的优化。王者上线的时候,市面上要求网络及时性强的即时PVP游戏是比较少的。我们做了各种各样的尝试,比如在网络做CPU方面的性能优化、延迟、丢包等等,网络本身花的时间是最多的。

架构上的微调,像刚才提到的中转模块,我们架构中大厅机器很多,PVP机器很多,架构中不需要每个进程知道详细信息,比如大厅服务器不需要知道后面有多少房间服务器,只需要知道后面有房间服务器,可以访问就OK。怎么划分、平衡负载、怎么屏蔽后端故障节点,都是由Proxy路由功能在负责,只是它最开始比较像三层结构,我们把下面两层划分成彼此之间没有交集的树枝的概念,每组Proxy只负责一部分的大厅和PVP服务器,因为这两种服务器在王者的服务器里面最多,但是后端都是通联,它们彼此之间不会有交互,但是当我需要发广播消息的时候,比如有一个人发了一个喇叭,需要让全服的人看到,就是由这个Proxy去转发给其他的Proxy,让对应的中转服务器发送给大厅服务器。

然后我们讲Proxy Adapter,我们上线后最开始上线只有四个大区,手Q、微信、Android、iOS四个环境,最开始Android的玩家不能和iOS的朋友开黑。开始Android和iOS分开也有一定原因,我们之前设想Android会先更新,iOS后跟进,以保持版本更新的稳定性。但后来我们希望Android和iOS的玩家可以因为关系链一起开黑。所以当Android、iOS版本更新频率一致时,我们希望不需要部署太多额外的机器资源和开发,直接利用Android和iOS已有的PVP服务器和大区资源,打通Android和iOS的pvp。当Android玩家登录Android大区会连接到Android大厅,iOS登录之后连接iOS大区的大厅,当他们需要开黑的时候,我们通过Adapter把中转模块所有的大区桥接起来,通过一定的算法投递到某个大区,投递的选择和大区资源占比有直接关系,因为Android手Q在王者里是最大的一个区,它所占用的机器也是最多的。

8、网络同步方案


之前做霸三国的时候采用CS的模式,就是服务器判定客户端表现。为什么我们在做王者的时候选用帧同步的方式呢?

CS模式的好处在于:首先是安全,因为都是服务器计算,客户端不管怎么作弊,它只是负责表现层面的功能,不会影响各种判定的结果。另外CLIENT-SERVER模式因为是基于结果的表现,所以中间可以出现丢包,丢包是可以被接受和处理的,只要最终的结果补发一致。帧同步在端游用得比较多,大家比较熟悉的dota,还有星际,都是用的帧同步技术。帧同步本身对网络要求更加严苛,下发的执行序列是不允许丢包的,需要严格保证顺序性,包是12345,就必须是12345,如果丢包,必须要等到丢的包完整到达之后才能顺序后续执行。MOBA本身的单位比较多,同屏时客户端最多有将近100个单位,假如一个AOE技能打到20个单位,然后中了一个debuff,CS状态模式需要发这些信息下去,可能潜在的同步状态信息是比较多的。

另外一个CS模式本身开发的方式,客户端表现与服务器的判定,要完美的匹配是比较困难的。比如我(的英雄)打出去一个法球,那服务器还要算这个运动是多久、客户端要跑多久,最后命中伤害了多少血。

我们之前做端游MOBA的时候,一个英雄的技能我们开发要两三周的时间。王者整个开发的时间是三四个月,这样的时间压力下面,我们用CS的方式搞不定,时间不够。不过当时心里也会比较紧张,因为当时市面上并没有看到用这种方式做的强PVP的高及时性的手游。帧同步本身网络抗抖动能力比较弱,因为不能丢包。帧同步的基本原理,大家有兴趣可以下来自己了解一下。一般会有区分,是网状还是主机模式。该技术的要点在于局内的运算都是基于客户端运算,10个人每个人各自算一份,有相同的起始、相同的输入、完全相同的中间运算逻辑,不存在真正的随机过程,这时候运算的结果,理论上应该是一致的。甚至包括浮点数运算都不应该存在,因为它有精度的问题。包括很多碰撞,动画,还有基本的数学运算库都是后台自己实现的,要去浮点整形化,避免客户端的本地逻辑,这是最容易犯的错误,这是出现不同步最常见的原因。如果某个经验不是很足的客户端程序,写程序时候用本地的代码做相应的逻辑,可能跑得越来越远,10个人都是平行的世界。

整体的网络结构,大体看来是分三层:

  • 1)服务器;
  • 2)客户端逻辑层;
  • 3)客户端表现层。

服务器主要负责的功能有两部分:一是收集所有玩家上行的输入,把它按定时的间隔打包成输入的序列,投放给所有客户端;当客户端出现丢包的时候,服务器进行补发;还有把客户端上行冗余的信息替换掉,比如有新的输入到了,就把老的输入Drop掉。王者我们的逻辑是66毫秒一次,1秒同步15个包,这是不能少的,因为帧同步不能丢包,数据包必须有严格的执行序列。

客户逻辑层理解为客户端本地的Sever,就是所有客户端运行的结果必须强一致,不能有真的随机,不能有本地逻辑,不能有浮点数的运算,就是10个客户端拿到相同的输入,产生结果必须一致。

客户端表现层是根据逻辑层的数据去做Copy或者镜像,然后在表现层进行平滑,帧数不一样,但是不会影响最终的运算结果,只影响动画和动作的表现。

PVP通信我们用的是UDP方式,最开始上线时我们是用的TCP技术,TCP在局域网的情况下表现还是不错的,没有什么问题,但是当外网出现丢包或者抖动的时候,受限于实现方式,比如窗口、慢启动各方面的原因,会发现当出现重联的时候会非常卡,所以后来我们没有用TCP,改为了采用udp。如果出现丢包,服务器会在应用层做补发。udp受限于MTU的大小,大于MTU,出现丢包就可能会出现整包的丢失。所以我们也会有些比较大的包会在APP层由服务器做分包,中间出现丢包再由服务器补发,把零碎的包拼成整包再做解包。比较有价值的是udp包,比如我们以服务器下行发给客户端来做,过程中如果手机因为信号抖动等情况,丢包是很明显的,这时候下发的时候通过冗余信息的方式降低丢包,是比较有效的解决方法。

帧同步的消息比较小,按照理论1秒15个驱动帧来算,20分钟的录像是10M左右。但是我们外网统计,正常的5V5对局20分钟,录像的大小大概是3M左右。服务器会把玩家的操作做纯内存的存储,当出现丢包的时候,服务器会通过编号快速找到缓存信息进行下发,同时根据丢包的情况,我们会计算给这个人发送冗余量的变化量。最开始发送每个包会冗余前面3帧的信息,如果丢包严重,我们会尝试冗余更多信息再下发。客户端拿到之后会尽量压缩逻辑执行的过程。帧同步有比较麻烦在于,它不像CS的模式随进随出,崩溃之后重回必须从一开始运行,中间运算过程不能少掉。

另外,我们也尝试过其他的一些方法,比如客户端上行之后,不需要服务器定时的间隔去做收集然后下发,我们发现这样做对手感提升的作用微乎其微,但它带来的负面作用其实更大,因为不是一秒定帧的15个包的下发,你可能下发包的数量非常多,完全和这个人的操作习惯有关系,有可能一个人一秒之内产生了十几二十个输入,就需要把这些输入染色编号之后对客户端下发。客户端因为收包很多,手机发烫就会很明显。我们也有和其他部门合作,做类似于tcp的技术,大家直观想到如果丢包就在io层做重发,但是实际的结果会发现,做的这个技术偏底层,所以对丢包的控制性不那么灵活,而且可能出来的结果还没有tcp本身好。

传统的帧同步的方式会做延迟投递,这个我们也有尝试过。如果间隔时间内出现丢包,或者出现包下行的时网络波动,可以通过延迟投递这种方式抹平抖和丢包的情况。我们尝试过这个方案但最终没有这样做的原因在于:王者里面一些英雄体验起来感觉偏动作,对反应要求比较快,延迟投递虽然确实抗抖动和抗丢包的能力不错,但是手感上达不到我们的要求。另外,做CS方式的实现,一般都会有一个套路,客户端提前表现,根据服务器的表现做平滑或者拉扯,这个方案我们也尝试过,但最终还是放弃了,因为这个技术会让角色本身的表现有点发飘。客户端本地一动,马上客户端表现就跟着动,但根据服务器的下行,其实会做一些偏移或者修正。当网络抖动出现的时候,你会发现这个角色有一点发飘,所以这个方案我们放弃掉了。

帧同步方案,所有客户端进行运算,期望产生一致的结果,但如果因为bug或者某个人使用修改器,跑出来的结果在某个时刻一定会和其他人不一样,当不一样出现,我们的说法是不同步了。我们会定时把一些关键信息提取出来做hash,不同步的人的hash和其他人会不一样。王者不同步率上线时大概是2%,也就是100局可能有2局出现一个人或者多个人运算结果和其他人不一样。我们现在把不同步做到万分之三,一万局里面只有三局出现这个情况。

这是怎么提升的呢?如果你用帧同步一定会遇到不同步的问题,客户端写错了,用了本地逻辑,可能浮点数的运算误差达到那样的临界点,它就会产生运算结果不一致。我们的方法有很多:自动化测试,用机器人不断跑,比如上新英雄之前,有脚本测试不断跑,看会不会产生不同步的结果;有专门的体验服、抢先服大区,发布到正式网络之前先测试,先小规模暴露问题,再解决问题;另外,当不同步的时候,我们会把这局整个录像和客户端间的log上传和保存下来,这样可以根据录像和中间执行的日志序列快速的定位是哪个地方出现问题。

我们对延迟和单局质量也有相应的监控,这一局有没有卡或者卡多少次,有没有出现丢包,丢包多少,最大的延迟、最大的抖动是多少,我们都是有相应的记录和统计。

同时,运营部的同学给我们提供了很多帮助,我们会有相关的网络测速、问题分析的SDK的合入。按照我们自己的经验,玩王者的同学肯定都卡过,但真的不是服务器的锅,服务器的性能没有问题,而且一直都是很低的负载。

主要的原因有几个:

  • 一是小区的带宽比较繁忙,很多小区其实都是公用带宽出口,比如有人在下电影、看直播,占用了很高带宽,你玩游戏就可能会卡;
  • 二是wifi路由器延迟比较高,家里的wifi路由器长期没有重启,因为它的代码也是人写的,就会有bug,就会存在终端过多、信道干扰、其他大流量的应用下载情况,这也会影响你玩王者;
  • 还有就是手机信号差、信号抖动,wifi、4G空口丢包等。

我们其实在网络优化上做了很多的尝试,例如根据丢包情况加大冗余,然后优化我们各方面执行的效率,去减少CPU的占用。王者后台方面,有两个点是我们一直努力在做的,网络优化和匹配机制,我们尝试用各种各样的方法,甚至后面也会尝试用AI深度学习的方法,来更加精准的定位玩家本身的真实水平,让他能够匹配到更加真实的同等水平的对手和队友,但网络这个问题还是现实存在,比如你进电梯玩王者它还是会卡。

附录:更多技术文章


[1] 有关架构设计的文章:
浅谈IM系统的架构设计
简述移动端IM开发的那些坑:架构设计、通信协议和客户端
一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
一套原创分布式即时通讯(IM)系统理论架构方案
从零到卓越:京东客服即时通讯系统的技术架构演进历程
蘑菇街即时通讯/IM服务器开发之架构选择
腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT
微信后台基于时间序的海量数据冷热分级架构设计实践
微信技术总监谈架构:微信之道——大道至简(演讲全文)
如何解读《微信技术总监谈架构:微信之道——大道至简》
快速裂变:见证微信强大后台架构从0到1的演进历程(一)
17年的实践:腾讯海量产品的技术方法论
移动端IM中大规模群消息的推送如何保证效率、实时性?
现代IM系统中聊天消息的同步和存储方案探讨
IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?
IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议
IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token
WhatsApp技术实践分享:32人工程团队创造的技术神话
微信朋友圈千亿访问量背后的技术挑战和实践总结
王者荣耀》2亿用户量的背后:产品定位、技术架构、网络方案等
>> 更多同类文章 ……

[2] 网络编程基础资料:
TCP/IP详解 - 第11章·UDP:用户数据报协议
TCP/IP详解 - 第17章·TCP:传输控制协议
TCP/IP详解 - 第18章·TCP连接的建立与终止
TCP/IP详解 - 第21章·TCP的超时与重传
技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)
通俗易懂-深入理解TCP协议(上):理论基础
通俗易懂-深入理解TCP协议(下):RTT、滑动窗口、拥塞处理
理论经典:TCP协议的3次握手与4次挥手过程详解
理论联系实际:Wireshark抓包分析TCP 3次握手、4次挥手过程
计算机网络通讯协议关系图(中文珍藏版)
UDP中一个包的大小最大能多大?
P2P技术详解(一):NAT详解——详细原理、P2P简介
P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解
P2P技术详解(三):P2P技术之STUN、TURN、ICE详解
通俗易懂:快速理解P2P技术中的NAT穿透原理
高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少
高性能网络编程(二):上一个10年,著名的C10K并发连接问题
高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了
高性能网络编程(四):从C10K到C10M高性能网络应用的理论探索
不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)
不为人知的网络编程(二):浅析TCP协议中的疑难杂症(下篇)
不为人知的网络编程(三):关闭TCP连接时为什么会TIME_WAIT、CLOSE_WAIT
不为人知的网络编程(四):深入研究分析TCP的异常关闭
不为人知的网络编程(五):UDP的连接性和负载均衡
不为人知的网络编程(六):深入地理解UDP协议并用好它
不为人知的网络编程(七):如何让不可靠的UDP变的可靠?
网络编程懒人入门(一):快速理解网络通信协议(上篇)
网络编程懒人入门(二):快速理解网络通信协议(下篇)
网络编程懒人入门(三):快速理解TCP协议一篇就够
网络编程懒人入门(四):快速理解TCP和UDP的差异
网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势
技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解
让互联网更快:新一代QUIC协议在腾讯的技术实践分享
现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障
聊聊iOS中网络编程长连接的那些事
移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”
移动端IM开发者必读(二):史上最全移动弱网络优化方法总结
>> 更多同类文章 ……

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:一个魔都程序员的3年:从程序员到CTO的历练下一篇:快速读懂量子通信、量子加密技术

本帖已收录至以下技术专辑

推荐方案
评论 6
游戏的网络通信实时性要求应该比IM苛刻吧,游戏里经常延迟卡或者掉线的话肯定会有人砸电脑
签名: 国庆长假还没有缓过来,请让我静一静,产品狗死远点...
好厉害的样子
引用:ztsabc 发表于 2019-01-21 15:46
从网络层面分析的很透彻, 做一款这么火爆的手游,底层的技术和框架都是需要深入考究的,写的非常好!

是的,技术积累非一朝一夕
看来路由还是需要经常重启的,要不影响开黑
好文章
签名: 开始码
先收藏吃灰,稍后被窝里看
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部