腾讯游戏4名技术专家详解:《御龙在天移动版》服务器性能优化
本文作者:蔡銘福、劉林、楊岳軍、向熠
一、游戲介紹
《御龍在天移動版》是一款3D MMORPG手游,以三國為背景,移植《御龍在天端游》經典玩法,主打手機上的實時萬人國戰,同時通過社交系統和主播語音來增進玩家間的交互。
游戲特點:合縱連橫,多國萬人同時參與國戰;開放式場景,隨時隨地遭遇戰;國家語音指揮,大量玩家集體行動。
二、性能優化背景
《御龍在天》手游的主打玩法是實時萬人國戰。
由于國戰的參與人數非常多,國戰的流程是分布在不同的地圖場景中。玩家在不同的場景間進行切換時,需要給玩家帶來良好的體驗。同時由于涉及多場景,如何做到降低邏輯編碼和正式環境部署的復雜度,提升正式環境擴容縮容的靈活性,考慮容災效果和負載均衡,需要一個設計合理的架構和場景切換流程。
國戰參與人數多帶來的另一個問題是流量消耗大。由于國戰期間,玩家是在地圖場景中實時進行移動和戰斗,大量釋放技能,玩家視野和屬性更新頻繁,勢必會有大量的網絡包需要發給手機客戶端。手機玩家對流量比較敏感,而玩家手機也參差不齊,大量的網絡包也會給手機端帶來性能和電量消耗,影響游戲流暢度。因此需要一些方法,對流量進行優化。
MMORPG游戲通常玩法和系統的數量都非常多,因此代碼量也是非常大的。當出現性能問題需要優化時,如何從百萬行級別代碼的工程里,發現那些性能浪費最嚴重的代碼,進行優化后,如何檢驗性能優化的效果,需要從工具、原因定位方法、優化策略幾個方面入手來解決問題。
基于這些問題背景,本文將從四個方面來介紹御龍在天手游的服務器優化經驗:
1.場景切換流程優化;2.國戰架構優化;3.流量消耗優化;4.代碼級性能優化工具、原因定位方法、優化策略。
三、場景切換流程優化
3.1 背景介紹
《御龍在天》手游是一款分場景地圖的MMORPG,不同國家的地圖場景,可能會分布在不同的zone場景進程上。當玩家從一個場景進入另一個場景時,如果兩個場景是分布在不同進程上,這時需要將玩家對象和相關數據、狀態信息,從原來所在進程同步到新的進程上,也就是我們說的跳zone流程。在這個流程中,由于涉及到玩家所在進程的切換,在網絡狀況欠佳的情況下,可能會導致重連失敗,影響玩家體驗。尤其是在移動網絡的環境下,受到運營商信號和網絡切換的影響,增加了跳zone失敗的幾率。基于以上原因,需要對跳zone流程進行優化,以降低跳zone失敗的幾率,提升玩家體驗。
3.2 原有方案
?
步驟如下:
原zone向目標zone發起占位請求;
目標zone向原zone回服占位響應;
占位成功后,原zone通知客戶端斷開原有連接;
客戶端發起新的網絡連接到目標zone。
方案缺點:
需要客戶端參與配合流程;
客戶端需要重新建立連接。
3.3 改進方案
?
步驟如下:
原zone向目標zone發起占位請求;
目標zone向原zone回服占位響應;
占位成功后,原zone通過agent,切換路由到目標zone的agent;
收到切換路由響應,數據同步到目標zone。
3.4 改進結果
目前項目使用改進后的方案,有2個優點:
不需要客戶端參與和配合進行跳zone流程,客戶端側完全無感知;
不需要重新建立網絡連接;
優化后的跳zone失敗率,從千分之一下降到了萬分之一以下,效果較為明顯。
四、國戰架構優化
4.1 背景介紹
國戰是《御龍在天》手游的核心玩法之一。由各國國王發起國戰,進攻方在指定國戰時間內,逐一占領敵國的邊境、陽平關、王城郊外,最后在王城內城殺死國家神獸,取得國戰勝利。每一個地圖都有一個要占領的目標點,進攻方通過殺死鎮守每個地圖的目標NPC,來占領該地圖。若在指定時間內,守方成功守衛本國神獸,則守方獲勝。國戰的架構設計和優化需要考慮幾個問題:
由于國戰涉及到多個場景和地圖,而不同的場景和地圖可能分布在不同的進程上,如何設計架構,以便在邏輯開發和外網部署時,不會依賴于具體的地圖,減少編碼復雜度,同時也可以根據運營數據靈活調整進程和機器數量;
由于王城內城的場景地圖,在非國戰期間是無法進入的,通過什么方式來實現,可以達到既節約機器資源,又能較好達到容災效果;
國戰期間參與玩家人數非常多,大量玩家聚集在同一個地圖和場景上,網絡流量陡增,如何設計場景進程zone和網絡連接管理進程tconnd的關系,使得在網絡層面減少對機器的壓力。
4.2 原有方案
?
場景進程zone與國家地圖綁定,通過配置進行關聯;
王城內城通過普通地圖方式實現,需要配置綁定到某個特定zone上;
每個zone進程綁定2個tconnd進程,每個zone進程上的所有客戶端連接都通過這2個tconnd。
缺點:
場景進程zone與國家地圖綁定,在國戰邏輯的開發時,對不同的邏輯需要做分支判斷,是否是在對應的地圖上;
王城通過普通地圖方式實現,在運營后期單區人數下降的時候,縮減機器也必須保留王城內城的zone。容災能力也較弱,如果王城內城的zone出故障,二手QQ交易就會影響國戰的流程,導致國戰無法正常進行;
zone與tconnd是綁定的,在國戰期間大量玩家聚集到一個zone上,會對對應的tconnd帶來很大壓力。
4.3 改進方案
場景進程zone與國家地圖不綁定,通過world進程在啟動時進行分配和創建;
王城內城通過副本的方式實現,在國戰開始時,通過副本創建策略,動態選擇zone進程來創建副本;
tconnd使用集群化模式,與zone進程不綁定,通過agent進程來路由消息;單區內所有zone進程的網絡連接,根據負載平均分布在所有tconnd上。
4.4 改進結果
場景進程zone與國家地圖不綁定,在國戰邏輯開發時,不需要再根據地圖來進行邏輯分支的判斷。在運營后期縮減機器和zone數量時,也非常靈活。
王城內城通過副本的方式實現,可以靈活分配選擇zone進程創建。同時也增強了容災能力,在國戰發起時,可以將王城內城副本創建在非故障的zone上。
tconnd使用集群化模式,與zone進程不綁定,玩家集中在某個zone上時,不會對tconnd造成壓力。同時還可以減少對外的ip和端口數量。
五、流量消耗優化
5.1 背景介紹
在移動網絡下,網絡流量也是RPG手游面臨的重要問題,特別在御龍在天國戰類RPG手游中顯得更為突出。御龍手游流量消耗的壓力主要來自兩個方面:a) 實時PVP,游戲場景實時更新,技能,屬性,移動廣播量大,發包頻繁; b) 國戰手游需要有宏大的場面,看到更多的人,但是視野大意味廣播量大,廣播包多。
國戰PVP中,手機客戶端會收到大量的技能包,視野包等,由于玩家手機參差不齊,手機上玩家對流量比較敏感,大量的網絡包不僅會帶來流量消耗,同時也會給手機端帶來CPU,GPU消耗,影響游戲流暢度。
因此需要我們在實時PVP中降低手機流量消耗,讓玩家不用擔心流量消耗,可以放心的在手機上盡情享受萬人國戰的快感。同時通過降低網絡流量,也能夠有效降低手機客戶端網絡包處理壓力,減少電量消耗,帶給玩家更流暢的游戲體驗。
5.2 流量優化措施
5.2.1 業務下行包合包
5.2.1.1 游戲下行包特點
發包頻率高,單個業務包小。以一測國戰數據為例,單個下行網絡包有效載荷不足50%。
5.2.1.2 合包策略
tconnd網關增加下行緩沖進行業務包合并,當緩沖區大小超過配置閾值或者等待時間超過配置時間時,將緩沖區包發送到網絡,多個業務包共用appolo頭,合并后壓縮。
對于需要實時下發的包,通過設置好實時下發標志,可以不用等待緩沖區滿或者超時,直接實時下發。
5.2.2 技能流量優化
一測下行數據占比
從統計數據來看,包量和包個數占比前三位的分別是技能效果,屬性通知,做技能包。屬性通知通常是伴隨著技能產生的,比如做技能中的BUFF對攻防的影響等等。通過對技能進行優化,可以有效降低網絡流量和包量。
5.2.2.1 技能施法流程
?
5.2.2.2 技能優化措施
1.技能效果分場景去廣播化處理,收斂廣播包量
如果是死亡包,因為需要視野內所有人看到,此時的效果包需要在視野內進行廣播。對于非死亡效果包,只廣播給攻擊者和受擊者。對于非攻擊和受擊玩家,血量同步通過主動pick查詢。
2.群戰做技能設定廣播閾值,到達域值廣播降級
如果是位移技能,視野中玩家需要依賴做技能包更新對應玩家位置信息,因此該技能包需要在視野中進行廣播。而對于非位移技能,在國戰中大量人群聚集群戰時,無需全視野廣播,只需要選擇部分玩家廣播做技能包,已經可以體現出戰場的激烈。具體措施如下:如果視野玩家不超過n人,做技能包按視野進行廣播,如果視野中玩家超過n人,則隨機從視野中挑選n個人進行做技能包廣播(包含技能受擊目標)。
3.技能效果和表現分離(一段傷害多段表現)
對于服務器下發的單次技能效果,客戶端通過分段的形式表現多段傷害,這樣可以在一次網絡包的情況下表現出多次受擊傷害效果,從而節省下發的技能效果包個數。
4.持續技能多段分時上報,去除按碰撞檢測上報
對于多段技能,采用分時上報的方式而不采用實時碰撞上報,通過分時合并上報目標,減少技能攻擊時上報的技能包個數。
5.2.3 移動流量優化
移動包降頻率
降低客戶端請求移動包頻率,客戶端行走預表現,增加服務器的移動誤差容忍值(初始容忍n秒誤差,大約m米距離誤差容忍)。
反作弊
加入移動反作弊,防止客戶端外掛,作弊次數多,降低容忍值。
5.3 流量優化結果
經過以上介紹的流量優化方法和措施,在視野人數增加了一倍的情況下,點將測試相比于第一次測試的流量消耗情況也得到了較大程度的優化。屬性包、技能包、技能效果包數量的占比,都下降為第一次測試時的三分之一到一半。同時,每秒業務包的數量減少了一半,每秒下行的流量減少為之前的大約四分之一。
六、代碼性能優化
6.1 背景介紹
MMORPG游戲通常玩法和系統的數量都非常多,因此代碼量也是非常大的。當出現性能問題需要優化時,如何從百萬行級別代碼的工程里,發現那些性能浪費最嚴重的代碼,進行優化后,如何檢驗性能優化的效果,需要從工具、原因定位方法、優化策略幾個方面入手來解決問題。
6.2 性能優化基礎工具
要在茫茫碼海中發現那些性能熱點,處理掉,再檢驗成效,需要一些基礎設施的支撐。御龍主要利用程序內部的性能統計系統以及一些常用的性能分析工具來發現代碼的性能熱點,使用靈活的機器人模型代碼優化迭代測試,并利用監控系統發現運營環境中的不易覺察的性能毛刺。
6.2.1 程序內部性能統計系統
游戲服務器代碼的執行一般有玩家行為驅動和服務器定時驅動兩種方式:
由玩家行為驅動時,可以在消息包處理的統一入口處統計消息包的數量、大小、處理時間等信息。
由定時器驅動時,可以把定時器做成動態注冊集中管理的方式,這樣也可以在定時器執行入口處統計各個定時器的執行次數和執行時間。
所有消息處理時間和定時器處理時間的總和基本上就是整個程序的總執行時間。
構建程序內部性能統計系統的好處有幾個:
有了各個消息包和定時器的執行時間,可以在業務層上進行調整盡可能降低cpu消耗,例如降低移動頻率和服務器定時驅動頻率等。
在使用性能分析工具對函數消耗進行排序時,會發現大量消耗時間很少的函數,形成長尾,不易逐一優化,程序內部的統計信息有利于在更高執行層次上進行整體優化。
另外,消息包和定時器的執行數量統計有利于建立機器人模型進行測試、對具體代碼的總執行時間進行預估等,程序的總執行時間的統計結果也有利于程序根據cpu情況進行自動調整服務。
6.2.2 性能分析工具
良好的性能分析工具可以讓性能優化事半功倍。御龍后臺在性能優化中嘗試過很多性能分析工具,包括gprof、oprofile、valgrind、perf、vtune等。使用哪種性能分析工具受到獲取簡便性、學習成本大小、是否滿足性能分析需求等影響,各種分析工具之間有不少差異,需要根據實際情況來選擇合適的分析工具。
選擇性能分析工具時有幾個因素需要特別考慮:
工具本身消耗的cpu大小。例如valgrind本身cpu消耗非常大,不適合使用于服務器負載較大時的性能分析,只適合于性能是線性增長的情況并作低負載測試。Oprofile、perf等受到系統內核支持的一般cpu消耗較低。
能否用于運營環境的性能分析。Gprof是使用編譯時嵌入代碼的方式,而且需要程序正常退出才能得到統計結果,oprofile需要重編系統內核支持,運營環境下使用不方便。
基于功能上的考慮。Gprof不能統計內核調用消耗,perf、vtune等基于硬件性能計數器的方式可以提供多種事件的統計分析結果,還有需要考慮可視化操作是否簡便等。
6.2.3 機器人測試驗證系統
服務器代碼執行是玩家行為驅動的,像視野廣播等性能消耗與玩家數量非線性關系的情況下,需要模擬大量玩家的群體性行為來測試代碼的性能優化結果。把機器人做到靈活可配置,可以根據程序內部的性能統計信息,配置機器人的登錄數量、各消息包發送頻率和外網一致,模擬運營環境的各種實際場景對優化結果進行迭代測試。
6.2.4 性能監控系統
除了國戰、集體過邊任務等可預見的場景會出現性能消耗高峰外,還可能在其它不易發現的場景中出現性能毛刺。Mmog游戲的功能非常豐富復雜,玩家群體行為難以完全預估,服務器機器數量有一兩千臺,這些因素都導致服務器性能毛刺發現困難。這時候就要通過加入服務器性能監控來發現潛在的性能問題。機器整體的cpu負載情況以及程序內部的性能統計信息都可以上報到公司的網管平臺,方便我們對各類性能信息做針對性監控。
6.3 原因定位方法
程序內部性能統計系統以及性能分析工具的運用,有助于我們更快發現程序的熱點所在,但是并不能幫助我們定位到哪一行需要修改的代碼,也不會告訴我們該如何作代碼修改,因此借助工具以外,我們仍然需要積累代碼優化的經驗。
6.3.1 熱點粒度
大多數性能分析工具都是以函數為單位進行性能統計的,有些分析工具如perf支持定位熱點到指令級上,但是要求有較高的采樣精度,需要系統內核的支持,并且帶來較高的性能損耗,不適宜使用到運營環境上。以函數作為性能統計單位時,如果一個函數代碼量過于龐大,就為定位性能熱點到代碼行帶來困難,因此熱點區域的代碼適宜寫成簡短的函數。
當無法定位熱點到函數中具體代碼行時,可以利用程序內部的性能統計系統在函數中若干位置插入統計代碼,根據統計日志逐步縮小熱點區域的范圍。
6.3.2 代碼分析
確定熱點代碼行的另一個重要手段是仔細分析代碼。一個函數成為熱點的原因可能有很多,包括函數本身的指令數量多且調用次數多、高頻調用帶來的棧管理開銷、磁盤操作、緩沖命中率低、分支預測失敗率高、缺頁、上下文切換等,需要根據具體代碼仔細分析性能消耗原因。也可以使用perf等性能事件統計結果進行輔助分析。
6.3.3 經驗數據
一行代碼是否可能夠成熱點,可以通過積累一些經驗數據進行計算確定。例如memset 1MB需要消耗多少時鐘周期、各種時間操作函數消耗的時間、文件操作消耗的時間等。積累各種代碼消耗的時間經驗數據后,可以根據統計日志等信息得到代碼的執行頻率,再計算代碼占用的cpu百分比。
6.4 代碼優化策略
定位到性能熱點并分析原因以后,需要根據具體代碼選擇合適的優化方向。下面將根據御龍后臺優化經驗談談常用的優化策略。
6.4.1 內存操作
MMOG后臺充斥著大量數據,御龍zone進程的虛擬存儲空間已經達到幾G級別,各種數據的存儲、修改、回寫、大數據包發送等潛伏著大量內存操作,對大內存的頻繁操作將會消耗較多cpu,因此內存操作的優化是經常使用的優化手段。下面以御龍遇到的其中一個內存操作的例子做說明。
在運營環境使用perf進行性能分析時發現,memset操作在所有函數性能消耗排名中排在第一位,其中技能定時器使用的memset耗時最多。
分析發現技能定時器中有定時發送持續傷害技能效果包的功能,在消息包字段填充前有memset整個包的操作,而由于這個包比較大,只要每秒發送幾百個技能效果包,就占用整體cpu的10%。這樣的技能包數據是比較容易達到的。
直接去掉memset可能隱含部分字段未初始化的風險,我們發現這個技能效果包的最大目標對象數定義為100個,而實際使用中是對單個對象使用的,最后我們通過縮小包的大小解決了這個問題。
6.4.2 時間操作
游戲服務器中經常會使用到一些時間操作函數,特別是在服務器定時檢查和驅動的操作中,可能需要遍歷每一個數據對象進行時間消逝檢查,時間操作函數是比較耗cpu的,如果對象數很多,而且檢查每個對象都調用localtime、time等獲取時間的函數,就會浪費較多cpu。
對一些對時間精度要求不是很高的操作,可以在一個定時器中獲取到時間并保存起來,其它需要使用時間的地方直接獲取這個緩存的時間即可。
時間函數的不合理使用將會耗費大量cpu,下面以御龍的一個例子作說明。
我們在檢查御龍國運時的性能統計日志時,發現一個任務相關的定時器處理耗時非常高,使得進程整體cpu在國運時也很高,使用vtune采集性能數據發現任務定時器中調用的一個時間相關的轉換函數耗時占整體進程的60%以上:
使用perf分析,發現_IO_vfscanf_internal和__offtime兩個函數耗時最高。但這兩個函數是標準庫函數,由于去掉了debug信息,無法看到調用棧情況。
使用pstack多次打印進程的棧信息,發現進程經常性執行到這兩個函數上,并且是從這個時間轉換函數中的__mktime_internal調用的。
因此熱點的耗時原因就較為清晰了,因為國運玩法中需要不斷定時檢查是否任務超時,在計算消逝時間時調用了開發框架庫中的一個時間轉換函數,這個函數實現時調用了標準庫的時候轉換函數__mktime_internal,而這個函數在字符串操作和時間計算等較為耗時。
最后我們使用了自己編寫的mktime時間轉換函數來解決這個問題,它是通過計算與已知時間點的差異來實現的,效率提升在20倍以上,完全消除了這個性能熱點。
6.4.3 cache-miss
由于游戲后臺存在大量的數據處理,很容易出現cpu緩存的cache-miss,造成程序執行效率的下降。使用perf的stat工具分析御龍國戰的zone性能,發現cache-miss情況比較嚴重。
這是與大量的內存數據引用和內存拷貝有關的。減少cache-miss的方法是使用局部性原理,減少存儲器變量的引用,或者降低整個內存操作的頻率,需要針對具體代碼進行優化。
下面是cache-miss引起性能嚴重下降的一個例子。在分析御龍聊天服性能的過程中,發現在消息廣播中一個非常簡單的獲取player身上一個字段的函數非常耗cpu。
把這個函數改成內聯后,cpu有所下降,但是整體cpu還比較高。Perf stat發現進程的cache-miss情況很嚴重。分析代碼發現聊天服上有幾萬個player對象,每次對一個國家范圍進行廣播時會遍歷所有幾萬個玩家找到對應國家的玩家進行廣播,以cache-miss消耗的時鐘周期和使用的cpu主頻來計算,每秒最高發幾百個消息包,由于國戰時文本消息廣播數量很多,每秒過百的消息包廣播請求是可能達到的。解決辦法是對玩家進行分國家管理,減少廣播時遍歷玩家的數量。
6.4.4 分支預測
分支預測失敗會破壞cpu的流水線操作,降低代碼執行效率。從御龍zone進程的perf stat分析結果來看,該進程有較高的分支預測失敗率:
可以使用perf工具指定branch-misses事件可以查看哪些函數的分支預測失敗率最高并進行優化。御龍使用perf對非國戰zone進行性能分析時發現,某個時間比較函數占用的時候排在第二位。
這個函數在branch-misses事件排序中排在前列。查看代碼發現函數實現非常簡單,但是里面包含一個計算和條件判斷較多的分支判斷,優化時我們把這個把函數里的分支判斷去掉,由上層調用保證傳入參數的正確性。優化后,這個函數就退出函數耗時榜前面位置。
6.4.5 文件操作
文件數據讀寫涉及磁盤操作,效率較低,這個容易預估。數據應該盡量在內存中進行緩存,減少頻繁的文件存取操作。另外有些隱秘的文件操作也可能導致性能問題。例如zone的vtune分析結果發現某個函數占用cpu較高。這個函數是判斷一個操作是否是敏感操作,對每一個上行數據包都會調用這個函數進行檢查,函數中有判斷一個含敏感操作列表文件是否存在的access操作。測試發現access操作在國戰高峰中調用次數非常多。最后我們通過分析后優化掉了這個access文件操作,使這個函數的cpu占用下降到可以忽略了。
6.4.6 高熱點代碼精簡
性能熱點的存在可以跟代碼本身的指令執行數量有關,因此精簡熱點函數的代碼是非常有用的優化措施。
例如御龍的視野廣播函數原來占用了較多cpu。在函數內部,遍歷視野列表、獲取視野對象、包發送等有較多安全性檢查以及異常處理的操作,便這些安全性檢查正常情況下不會出現,或者在其它代碼中已經做了安全性保證,或者出現異常不影響后續邏輯的繼續執行,所以可以直接把這個代碼精簡掉。由于視野廣播執行很頻繁,每次廣播都要遍歷整個視野代碼,循環內的代碼執行頻次非常高,所以精簡代碼的優化措施非常有效。
6.4.7 內聯
簡短函數的高繁次調用會帶來棧管理的開銷,很容易想到把這些函數改成內聯函數。一些日志打印、狀態檢查等函數調用頻次非常高,如果使用的總cpu較高,就可以改為內聯函數,御龍在這方面的例子包括前面6.4.3中的GetStatus函數和6.4.4中的時間比較函數,都取得了較好的優化效果。
七、結語
通過以上的優化方法、工具和策略,《御龍在天》手游服務器在以下幾個方面都達到了理想的優化效果:
通過場景切換流程優化,大大降低了玩家場景切換失敗的幾率;
通過國戰架構優化,降低了邏輯開發復雜度,提升了正式環境部署的靈活性和容災效果,同時也均衡了網絡流量對機器帶來的壓力;
通過合包策略和技能、移動流量優化,降低了客戶端側的流量消耗,也提升了客戶端的運行流暢度;
在代碼級性能優化部分,通過結合御龍在天手游的實際運營案例,介紹了在服務器側常用的性能優化工具、原因定位方法和優化策略,希望能給各位讀者在遇到性能優化問題時提供相關幫助。
總結
以上是生活随笔為你收集整理的腾讯游戏4名技术专家详解:《御龙在天移动版》服务器性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 游戏中的整容术! 《Honey Sele
- 下一篇: 《穿越火线:枪战王者》手游客户端技术方案