風靡全國,日活8000萬,《王者榮耀》后臺技術架構演進!
《王者榮耀》能夠成為如今國內最成功的手游,其背后成熟的技術團隊支持可以說是功不可沒。
這個曾經在端游時代主導搭建 RTS 游戲《霸三國》框架的技術團隊,在轉型做 MOBA 手游《王者榮耀》后為游戲提供了巨大的支持,但這個過程也并非一帆風順。
在今年剛結束的騰訊 TGDC 上,《王者榮耀》技術總監孫勛在技術專場中,對這款游戲進行了一次技術復盤,從技術層面上為聽眾嘉賓講解了游戲在引擎、整體網絡架構與網絡同步方案上的嘗試與轉變。
孫勛稱,目前游戲的服務器架構主要由“游戲大廳”和“PvP”2 個部分組成,而在不斷探索中,后來又在架構中加入了 Proxy 中轉服務器,也正是這個服務器的加入為《王者榮耀》解決了后來“安卓、iOS”同服等一系列出現的問題。
此外,他還介紹了《王者榮耀》在網絡協議以及同步方案上的一些嘗試,并一一復盤了這些嘗試的優劣勢。
為大家解答了為什么,最終游戲會放棄 TCP 協議(傳輸控制協議)與曾經在《霸三國》中所使用的 Client-Server 結構(C/S結構),并且轉而使用了 UDP 協議(用戶數據報協議)與幀同步方案。
本文是騰訊王者榮耀項目技術總監孫勛帶來的《王者榮耀技術架構》主題演講內容整理。將分幾部分為大家介紹王者后臺開發過程中的一些內容和思考:包括《王者榮耀》整個背景介紹、后端架構、上線后的調整,以及網絡同步方案和反作弊方案等。
現在《王者榮耀》后端機器大概有 4600 多臺,我們的容量也有一定的擴展,進程數目是 4 萬多個。
《王者榮耀》游戲背景
2012 年,我們當時做的端游《霸三國OL》,就是王者的前身。這款產品最開始是偏向 RTS 的游戲,后來我們把它改成了端游 MOBA,再后來做成了手游 MOBA,即現在的《王者榮耀》。
從 2012 年開始做 RTS 游戲到 2013 年,從多控制單位的 RTS 游戲,變成 MOBA 游戲,到 2014 年啟動手游 MOBA 的預研,再到 2015 年 2 月份我們把大量人力(大概100多號人)投入做《英雄戰跡》(《王者榮耀》前身)開發,時間并不長。
《霸三國》的玩法是玩家可以在戰前通過排兵布陣構成自己局內的策略,通過控制多個單位,技能釋放、兵種特性的釋放形成對抗。
我們最開始做《霸三國》的時候客戶端引擎是 unreal,但在做《王者榮耀》的時候改用了unity 引擎,3 到 4 個月的研發時間內,產品本身從代碼層面沒有任何東西是從《霸三國》那里搬過來用的,全部代碼都需要重寫。
《霸三國OL》的一些啟示
做端游《霸三國OL》的這段經歷,給我們做王者帶來很多相應的啟示,比如策劃、程序及整個團隊對 MOBA 的理解。
另外當時在做端游《霸三國》的時候,我們采用了 Client-Server 的模式,但其實在過程中有借鑒類似幀同步的概念:例如在斷線重回對視野的處理這塊。
傳統的做法是,重回時會發當前的鏡像和后續的其他下行通知信息。
這種做法會有一個問題,如果新增其他的場景內模塊的時候,根據場景內包含的當前的各種物件、所在狀態的各種各樣信息,都需要把這些東西打包發下去,在后續開發、維護的時候會顯得很麻煩。
我們的做法是,把服務器下發的所有序列包做緩存,并按順序重發,讓客戶端做出快進的表現,它的概念和幀同步比較類似。
還有一點,就是預留設計彈性,在最開始的 RTS 中,每個玩家最多可以操作 5-8 個單位進行對抗,到后來改成 MOBA 游戲,只能操作一個英雄,并且加入各種各樣的場景,我們本身的技術框架并不需要做出顛覆性的改動。
《王者榮耀》整體架構
目前《王者榮耀》后臺的整體架構設計是源自產品的需求。如果大家玩過《王者榮耀》就會知道,PvP 對抗是不分區服的。
微信 1 區的玩家可以和微信 2 區玩家一起對抗,甚至 iOS 平臺也可以和 Android 平臺的人一起玩,但同時一些共有地方也保留了分區概念,比如戰隊、排行榜是基于“區”概念的。“區”在游戲里面就是編號,可以理解為打在玩家新建角色上的 Logo。
我們最開始做架構實現的時候,服務器當時做得比較簡單,從原型開始只是保留了大廳和 PvP 服務器這兩塊,兩者是分開的。
PvP 服務器使用類似 CGI 調用,可以分配資源的使用,用完之后再回收,不負責其他的東西。需要的東西從大廳拿,用了之后回給大廳,讓大廳回寫 DB。
我們在大廳和 PvP 之間做直聯,后來把直聯改成了中間轉發,在《王者榮耀》里面我們叫 Proxy,相當于代理服務器,以屏蔽本身后端很多進程分布的細節。因為游戲本身的機器、進程很多,還有不同的路由規則。
某些排行榜或者戰隊是根據邏輯區的編號來確定哪臺機器,或者多臺機器進行處理的。有些消息采用隨機轉發或者多發廣播的方式,這些都是由 Proxy 負責路由。之后又加入了房間服務器,它負責的是《王者榮耀》內匹配、排位等相關功能。
怎么樣把實力比較接近的人糅合到一塊兒玩,是由房間匹配服務器來做相應的負責的,因此會有戰隊和其他服務器戰隊匹配到一起。
最后我們在上面加入了一個 Adapter,作用是用本身已經部署的大區資源實現跨服匹配的功能。
游戲的后端架構,除了戰隊這樣的服務器之外,所有其他的模塊都可以在線擴容,或者在發現有引起在線下降的故障時,從整個架構里自動屏蔽掉。
因為路由方式會限定比如一區、二區、三區到這臺機器處理,如果故障,影響的只是某幾個邏輯區玩家請求的處理,降低故障影響范圍。
《王者榮耀》目前的機器數量,可能每周都會發現有機器壞掉,至少有一臺機器宕掉,在架構里面保證模塊自動屏蔽,和在線擴容,是非常重要的事情。
整體結構比較像 MMO 的三層結構,MMO 在騰訊有比較典型的三層級別結構。大廳服務器會根據玩家所在區,登錄具體區的大廳服務器。
單個大廳進程可以承載 2 萬人,單個 PvP 可以承載 1.2 萬,小區登錄微信一區還是二區就是角色 Logo,打在玩家身上。
《王者榮耀》現在外網有四個大區,比如 Android 手 Q、Android 微信、iOS 手 Q、iOS 微信,此外還有搶先服。
我們會用程序開關的方式,在大版本發布之前,優先更新搶先服,這時候它不能和正式服玩家匹配在一起,因為他們的版本不一致。當全服發布之后,它的版本更新一致之后,我們會打開開關,搶先服的玩家可以和正式服的玩家一起進行 PvP 的匹配。
除此之外,我們還有專門的體驗服,是給策劃驗證相關設計的,體驗服保留可能刪檔的操作,但在正式環境這是絕對不允許的。
另外,以前的傳統手游偏單機,就會做很多協議兼容,客戶端版本沒有更新可以玩。但是《王者榮耀》里的主要玩法是 PvP,同時結合實現方式,不同版本的玩家不能匹配一起,所以我們沒有做多版本協議兼容。
上線后的調整
上線后,《王者榮耀》本身的后臺架構,整體上沒有做太大的改動,因為我們做端游的時候,對這套結構比較清楚,我們知道哪個地方可能有什么樣的問題,所以整個結構一直比較穩定。
但是我們做了相應的微調,做得最多的是網絡本身的優化。《王者榮耀》上線的時候,市面上要求網絡及時性強的即時 PvP 游戲是比較少的。
我們做了各種各樣的嘗試,比如在網絡做 CPU 方面的性能優化、延遲、丟包等等,網絡本身花的時間是最多的。
架構上的微調,像剛才提到的中轉模塊,我們架構中大廳機器很多,PvP 機器很多,架構中不需要每個進程知道詳細信息,比如大廳服務器不需要知道后面有多少房間服務器,只需要知道后面有房間服務器,可以訪問就 OK。
怎么劃分、平衡負載、怎么屏蔽后端故障節點,都是由 Proxy 路由功能在負責。因為大廳、PvP 機器太多,我們通過 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 把中轉模塊所有的大區橋接起來,通過一定的算法投遞到某個大區。投遞的選擇和大區資源占比有直接關系。
網絡同步方案
之前做《霸三國》的時候采用 Client-Server 的模式,服務器判定客戶端表現,那為什么我們在做《王者榮耀》的時候選用幀同步的方式呢?
Client-Server 模式的好處在于:
首先,安全。因為都是服務器計算,客戶端只是負責表現層面的功能,不會影響各種判定的結果。
另外,Client-Server 模式因為是基于結果的表現,所以中間可以出現丟包,丟包是可以被接受和處理的,只要最終結果補發一致即可。
幀同步在端游用得比較多,大家比較熟悉的 DotA,還有《星際爭霸》,都是用的幀同步技術。
幀同步本身對網絡要求更加嚴苛,下發的執行序列是不允許丟包的,需要嚴格保證順序性,包是 12345,就必須是 12345,如果丟包,必須要等到丟的包到達之后才能順序后續執行。
MOBA 本身的單位比較多,同屏時客戶端最多有將近 100 個單位,假如一個 AOE 技能打到 20 個單位,然后種了一個 debuff,Client-Server 狀態模式需要發這些信息下去,可能潛在的同步狀態信息是比較多的。
另外一個 Client-Server 模式本身開發的方式,客戶端表現與服務器的判定,要完美的匹配是比較困難的。
我們之前做端游 MOBA 的時候,一個英雄技能我們開發要兩三周的時間。《王者榮耀》當時開發周期是三、四個月,這樣的時間壓力下,我們用 Client-Server 的方式搞不定,時間不夠。
當時團隊心里會比較緊張,因為那時候市面上并沒有看到用這種方式做強 PvP、高及時性手游的。
幀同步網絡抗抖動能力比較弱,因為不能丟包。幀同步的基本原理,大家有興趣可以下來自己了解一下。
一般會有區分,是網絡還是主機模式。該技術的要點在于局內的運算都是基于客戶端運算,10 個人中,每個人都會各自算一份,有相同的起始、相同的輸入、完全相同的中間運算邏輯,不存在隨機過程,這時候運算的結果,理論上應該是一致的。
甚至包括浮點數運算都不應該存在,它有精度的問題。包括很多碰撞,動畫,還有基本的數學運算庫都是后臺自己實現的,要去浮點整形化,避免客戶端的本地邏輯,這是最容易犯的錯誤,這是出現不同步最常見的原因。
如果某個經驗不是很足的客戶端程序,寫程序時候用本地的代碼做相應的邏輯,可能跑得越來越遠,10 個人都是平行的世界。
整體的網絡結構,大體看來分三層:服務器、客戶端邏輯層,客戶端表現層。
服務器主要負責的功能有兩部分:
收集所有玩家上行的輸入,把它按定時的間隔打包成輸入的序列,投放給所有客戶端。
當客戶端出現丟包的時候,服務器進行補發;還有把客戶端上行冗余的信息替換掉,比如有新的輸入到了,就把老的輸入 Drop 或者替換掉。
在《王者榮耀》里,我們的邏輯是 66 毫秒一次,1 秒同步 15 個包,這是不能少的,因為幀同步不能丟包,數據包必須有嚴格的執行序列。
客戶端邏輯層理解為客戶端本地的服務,就是所有客戶端運行的結果必須強一致,不能有真的隨機、不能有本地邏輯、不能有浮點數運算。拿到相同的輸入,產生結果必須一致。
客戶端表現層會根據邏輯層的數據去做 Copy 或者鏡像,然后在表現層進行平滑,幀數不一樣,但是不會影響最終的運算結果,只影響動畫和動作的表現。
PvP 最開始上線時,我們用的是 TCP 技術。TCP 在局域網的情況下表現還是不錯的,沒有什么問題,但是當外網出現丟包或者抖動的時候,受限于實現方式。
比如窗口、慢啟動各方面的原因,會發現當出現重連的時候游戲非常卡,所以后來我們沒有用 TCP,改為了采用 UDP。如果出現丟包,服務器會在應用層做補發。
UDP 受限于 MTU(最大傳輸單元)的大小,大于 MTU,會出現分包,可能也會出現整包的丟失。
所以我們也會有些比較大的包會在 App 層由服務器做分包,中間出現丟包再由服務器補發,把零碎的包拼成整包再做解包。
比較有價值的是 UDP 包,如果手機因為信號抖動等出現丟包,下發的時候通過冗余方式,是比較有效的解決方法。
幀同步的消息比較小,按照理論 1 秒 15 個驅動幀來算,20 分鐘的錄像是 10M 左右。但是我們外網統計,正常的 5V5 對局 20 分鐘,錄像的大小大概是 3M 左右。
服務器會把玩家的操作做純內存的存儲,當出現丟包的時候,服務器會通過編號快速找到緩存信息進行下發。同時根據丟包的情況,我們會計算給這個人發送冗余量的變化量。
最開始發送每個包會冗余前面3幀的信息,如果丟包嚴重,我們會嘗試冗余更多信息再下發。客戶端拿到之后會盡量壓縮邏輯執行的過程。
幀同步有比較麻煩的模式在于,它不像 Client-Server 的模式隨進隨出,崩潰之后重回必須從一開始運行,中間運算過程不能少掉。
當然,我們也嘗試過其他的一些方法。比如客戶端上行之后,不需要服務器定時的間隔去做收集然后下發,而是通過染色幀編號直接下發,這樣響應更及時,操作反饋更強、更快。
當時我們做出來的結果是,這對手感的提升微乎其微,但是帶來的負面問題卻很大,因為不再是一秒 15 個包固定的下發,下發包的數量非常多,完全和這個人的操作習慣有關系。
有可能一個人一秒之內產生了十幾二十個輸入,就需要把這些輸入打包之后對客戶端下發。客戶端因為收包很多,設備也會明顯發燙。
我們也有和其他部門合作,做類似于 TCP 的技術,大家直觀想到如果丟包就在 IO 層做重發。
但是實際的結果會發現,做的這個技術偏底層,所以對丟包的控制性不那么靈活,而且可能出來的結果還沒有 TCP 本身好。
傳統的幀同步的方式會做延遲投遞,這個我們也有嘗試過。如果間隔時間內出現丟包,或者出現包下行時的網絡波動,可以通過延遲投遞這種方式抹平抖動和丟包的情況。
我們嘗試過這個方案但最終沒有這樣做的原因在于:《王者榮耀》里面一些英雄體驗起來感覺偏動作,對反應要求比較快,延遲投遞雖然抗抖動和抗丟包的能力確實不錯,但是手感上達不到我們的要求。
另外,做 Client-Server 方式的實現,一般都會有一個套路,客戶端提前表現,根據服務器的表現做平滑或者拉扯。
這個方案我們也嘗試過,但最終還是放棄了,因為這個技術會讓角色本身的表現有點發飄。
客戶端本地動,馬上客戶端表現就跟著動,但根據服務器的下行,其實會做一些偏移或者修正。當網絡抖動出現的時候,角色會有一點發飄,所以這個方案我們放棄掉了。
幀同步方案,所有客戶端進行運算,期望產生一致的結果,但如果因為 Bug 或者某個人使用修改器,跑出來的結果會和其他人不一樣,當不一樣出現,我們的說法是不同步了。
我們會定時把一些關鍵信息提取出來做 Hash,不同步的人的 Hash 和其他人會不一樣。
《王者榮耀》不同步率上線時大概是 2%,也就是 100 局可能有 2 局出現一個人或者多個人結果和其他人不一樣。我們現在把不同步率做到了萬分之三,一萬局里面只有三局出現這個情況。
這是怎么提升的呢?如果你用幀同步一定會遇到不同步的問題,客戶端寫錯了,用了本地邏輯,可能浮點數的運算誤差達到那樣的臨界點,它就會產生運算結果不一致。
我們的方法有很多:自動化測試,用機器人不斷跑,比如上新英雄之前,有腳本測試不斷跑,看會不會產生不同步的結果;有專門的體驗服、搶先服大區,發布到正式網絡之前先測試,先暴露問題,再解決問題。
另外,當不同步的時候,我們會把這局整個錄像和客戶端間的 Log 上傳和保存下來,這樣可以根據錄像和中間執行的日志序列快速的定位是哪個地方出現問題。
我們對延遲和單局質量也有相應的監控,這一局有沒有卡或者卡多少次,有沒有出現丟包,丟包多少,最大的延遲、最大的抖動是多少,我們都是有相應的記錄和統計。
運營部的同學給我們提供了很多幫助,我們會有相關的網絡測速、問題分析的 SDK 的合入。
按照我們自己的統計,游戲卡頓主要的原因有幾個:
- 小區的帶寬比較繁忙,很多小區其實都是公用帶寬出口,比如有人在下電影、看直播,占用了很高帶寬,你玩游戲就可能會卡。
- Wi-Fi 路由器延遲比較高,家里的 Wi-Fi 路由器長期沒有重啟,就會存在終端過多、信道干擾、其他大流量的應用下載情況,這也會影響你玩《王者榮耀》。
- 手機信號差、信號抖動,Wi-Fi、4G 空口丟包等。
我們在網絡優化上做了很多的嘗試,例如根據丟包情況加大冗余,然后優化我們各方面執行的效率,去減少 CPU 的占用。
《王者榮耀》后臺方面,有兩個點是我們一直努力在做的,網絡優化和匹配機制,我們嘗試用各種各樣的方法,甚至后面也會嘗試用 AI 深度學習的方法,來更加精準的定位玩家本身的真實水平,讓他能夠匹配到更加真實的同等水平的對手和隊友。
孫勛
騰訊王者榮耀項目技術總監
2005 年加入騰訊,最開始不是做游戲,2007 年前一直做拍拍網,2007 年加入成都臥龍工作室,也就是現在的天美 L1 工作室。之前參與過《QQ三國》、《封神記》、《霸三國OL》,到后來的《王者榮耀》,現在是這款游戲的技術總監。




























