老出BUG怎麼辦?遊戲伺服器常見問題的解決方法分享

遊資網發表於2019-10-17
在遊戲開發中,我們經常會遇到一些技術難題,而其引發的bug則會影響整個遊戲的品質。女性向手遊《食物語》就曾遇到過一些開發上的難題,騰訊遊戲學院專家團Wade、Zc、Jovi等專家為其提供了指導和幫助。

老出BUG怎麼辦?遊戲伺服器常見問題的解決方法分享

過載保護、叢集、伺服器通訊、併發選型等方面的問題,是中小團隊常常的技術難題,本文分享了一些專家在坐診過程中提到的解決方法,希望對大家有所幫助。

問題一:玩家登入時拉取好友資訊,但好友服務繁忙導致登入失敗。

解決方法:

1、分離關鍵路徑上非關鍵呼叫,縮短事務流程,避免周邊服務異常阻塞登入。

2、服務熔斷機制,超出處理能力快速失敗,防止雪崩。

3、按使用者隔離事務,避兔單個使用者請求阻塞影響到其他使用者。

問題二:壓測併發登入對redis產生很大壓力。

解決方法:redis資料表數量多,一次事務會產生多個redis請求,小表合併為大表。

Wade:伺服器程式的管理一般比較簡單,有很多還是用配置檔案靜態組織的。同時往往程式間通訊的手段比較缺乏,沒有使用訊息佇列中介軟體,甚至還有用Redis來做通訊元件使用的。為了提高叢集管理的自動化水平,使用ZooKeeper是一個比較常見的方法。

Zc:redis一般做為記憶體快取來使用,不宜將關鍵資料存放在redis中.其資料安全性並不如一般的DB。在使用過程中也需要參考效能基線,控制訪問頻率和流量。

問題三:外部服務有延遲,呼叫到的業務流程中產生卡頓。

解決方法:業務側增加快取:同玩好友msdk+最近角色id+角色資訊。

Wade:很多團隊對於過載保護不夠重視,往往只在最外層接入客戶端一側有最大連線數或者最大會話數的限制。而對於內部的多個程式,比如訪問資料庫的程式,就沒有太多的負載保護。由於遊戲是帶狀態的程式比較多,所以負載均衡往往也做的不多,基本上是按狀態所在程式去轉發處理請求。

Zc:注意快取和降級處理。外部平臺資料,儘量快取,提高訪問體驗。當發現外部服務出現故障,或本身出現負載風險時,應降級服務。

Jovi:msdk midas平臺特權等api接入工作,遊戲業務可以建立一個隔離層專門處理這塊需求,避兔過分侵入遊戲邏輯,更容易控制。

問題四:運營和客服介面修改玩家資料,會與正常遊戲的資料回寫產生競爭。

解決方法:使用類似郵件機制去修改資料。

Zc:多執行緒開發中,經常會有執行緒池用盡或執行緒死鎖導致服務質量下降。建議將執行緒池根據業務需求合理分類,不同業務間有合理的負載配比,不會相互影響。非關鍵流程需要延後或者非同步化處理,避免卡死關鍵流程。

同時,合理的執行緒模型可以有效減少執行緒間競爭。對確實需要競爭的資源在流程入口處統一有序加鎖,避免在邏輯過程中,隨意巢狀取鎖競爭。並且,給鎖加個超時時間,避免業務中斷。

Jovi:確保同一時刻只有單個資料修改點,有助於避免資料競爭。建議設計時採用CQRS方式,採用獨立的資料表和服務記錄事件,彙總到單一修改服務上執行。

Wade:併發程式設計是伺服器端最常見的問題,一般會用多執行緒或者非阻塞兩種方法之一解決。對於天然支援多執行緒的語言,如JAVA,很多開發者傾向多執行緒,好處是程式碼編寫起來比較方便,但是這就要很清醒的對各種物件進行鎖的操作,或者熟練使用類似java.util.concurrent這種多執行緒工具庫。而如果使用非阻塞,好處是不會有鎖的問題,但程式碼被分割到各個回撥函式中,可讀性非常糟糕,所以有的團隊會使用“協程”或者Promise之類的工具來緩解這個問題,但這也引入了更多的複雜性。

下面詳細介紹一下游戲伺服器端架構中的排程架構,方便大家理解。

a)單程式遊戲伺服器

最簡單的遊戲伺服器只有一個程式,是一個單點。這個程式如果退出,則整個遊戲世界消失。在此程式中,由於需要處理併發的客戶端的資料包,因此產生了多種選擇方法:

老出BUG怎麼辦?遊戲伺服器常見問題的解決方法分享

[圖-單程式排程模型]

  • 同步-動態多執行緒


每接收一個使用者會話,就建立一個執行緒。這個使用者會話往往就是由客戶端的TCP連線來代表,這樣每次從socket中呼叫讀取或寫出資料包的時候,都可以使用阻塞模式,編碼直觀而簡單。有多少個遊戲客戶端的連線,就有多少個執行緒。但是這個方案也有很明顯的缺點,就是伺服器容易產生大量的執行緒,這對於記憶體佔用不好控制,同時執行緒切換也會造成CPU的效能損失。更重要的多執行緒下對同一塊資料的讀寫,需要處理鎖的問題,這可能讓程式碼變的非常複雜,造成各種死鎖的BUG,影響伺服器的穩定性。

  • 同步-多執行緒池


為了節約執行緒的建立和釋放,建立了一個執行緒池。每個使用者會話建立的時候,向執行緒池申請處理執行緒的使用。在使用者會話結束的時候,執行緒不退出,而是向執行緒池“釋放”對此執行緒的使用。執行緒池能很好的控制執行緒數量,可以防止使用者暴漲下對伺服器造成的連線衝擊,形成一種排隊進入的機制。但是執行緒池本身的實現比較複雜,而“申請”、“釋放”執行緒的呼叫規則需要嚴格遵守,否則會出現執行緒洩露,耗盡執行緒池。

  • 非同步-單執行緒/協程


在遊戲行業中,採用Linux的epoll作為網路API,以期得到高效能,是一個常見的選擇。遊戲伺服器程式中最常見的阻塞呼叫就是網路IO,因此在採用epoll之後,整個伺服器程式就可能變得完全沒有阻塞呼叫,這樣只需要一個執行緒即可。這徹底解決了多執行緒的鎖問題,而且也簡化了對於併發程式設計的難度。但是,“所有呼叫都不得阻塞”的約束,並不是那麼容易遵守的,比如有些資料庫的API就是阻塞的;另外單程式單執行緒只能使用一個CPU,在現在多核多CPU的伺服器情況下,不能充分利用CPU資源。非同步程式設計由於是基於“回撥”的方式,會導致要定義很多回撥函式,並且把一個流程裡面的邏輯,分別寫在多個不同的回撥函式裡面,對於程式碼閱讀非常不利。——針對這種編碼問題,協程(Coroutine)能較好的幫忙,所以現在比較流行使用非同步+協程的組合。不管怎樣,非同步-單執行緒模型由於效能好,無需併發思維,依然是現在很多團隊的首選。

  • 非同步-固定多執行緒


這是基於非同步-單執行緒模型進化出來的一種模型。這種模型一般有三類執行緒:主執行緒、IO執行緒、邏輯執行緒。這些執行緒都在內部以全非同步的方式執行,而他們之間通過無鎖訊息佇列通訊。

b)多程式遊戲伺服器

多程式的遊戲伺服器系統,最早起源於對於效能問題需求。由於單程式架構下,總會存在承載量的極限,越是複雜的遊戲,其單程式承載量就越低,因此開發者們一定要突破程式的限制,才能支撐更復雜的遊戲。

一旦走上多程式之路,開發者們還發現了多程式系統的其他一些好處:能夠利用上多核CPU能力;利用作業系統的工具能更仔細的監控到執行狀態、更容易進行容災處理。多程式系統比較經典的模型是“三層架構”:

在多程式架構下,開發者一般傾向於把每個模組的功能,都單獨開發成一個程式,然後以使用程式間通訊來協調處理完整的邏輯。這種思想是典型的“管道與過濾器”架構模式思想——把每個程式看成是一個過濾器,使用者發來的資料包,流經多個過濾器銜接而成的管道,最後被完整的處理完。由於使用了多程式,所以首選使用單程式單執行緒來構造其中的每個程式。這樣對於程式開發來說,結構清晰簡單很多,也能獲得更高的效能。

老出BUG怎麼辦?遊戲伺服器常見問題的解決方法分享
[圖-經典的三層模型]

儘管有很多好處,但是多程式系統還有一個需要特別注意的問題——資料儲存。由於要保證資料的一致性,所以儲存程式一般都難以切分成多個程式。就算對關係型資料做分庫分表處理,也是非常複雜的,對業務型別有依賴的。而且如果單個邏輯處理程式承載不了,由於其記憶體中的資料難以分割和同步,開發者很難去平行的擴充套件某個特定業務邏輯。他們可能會選擇把業務邏輯程式做成無狀態的,但是這更加加重了儲存程式的效能壓力,因為每次業務處理都要去儲存程式處拉取或寫入資料。

除了資料的問題,多程式架構也帶來了一系列運維和開發上的問題:首先就是整個系統的部署更為複雜了,因為需要對多個不同型別程式進行連線配置,造成大量的配置檔案需要管理;其次是由於程式間通訊很多,所以需要定義的協議也數量龐大,在單程式下一個函式呼叫解決的問題,在多程式下就要定義一套請求、應答的協議,這造成整個原始碼規模的數量級的增大;最後是整個系統被肢解為很多個功能短小的程式碼片段,如果不瞭解整體結構,是很難理解一個完整的業務流程是如何被處理的,這讓程式碼的閱讀和交接成本巨高無比,特別是在遊戲領域,由於業務流程變化非常快,幾經修改後的系統,幾乎沒有人能完全掌握其內容。

關於騰訊遊戲學院專家團

如果你的遊戲也富有想法充滿創意,如果你的團隊現在也遇到了一些開發瓶頸,那麼歡迎你來聯絡我們。騰訊遊戲學院聚集了騰訊及行業內策劃、美術、程式等領域的遊戲專家,我們將為全世界的創意遊戲團隊提供專業的技術指導和遊戲調優建議,解決團隊在開發過程中遇到的一系列問題。

專案指導合作請聯絡微信:18698874612


來源:騰訊GWB遊戲無界
原地址:https://mp.weixin.qq.com/s/cl_pRME_oMN6ipCSqZ4snA

相關文章