《大唐豪俠》架構開發紀實

 源:http://hi.baidu.com/let163/blog/item/6742d4f7bb93682ebc310983.html

 

遊戲介紹

《大唐豪俠》是網易公司2006年主推的一款網絡遊戲。它是一款以唐朝爲背景的武俠遊戲,採用即時戰鬥機制。遊戲創作人員在《大唐豪俠》立項後,都表現得躍躍欲試,急欲一展身手。根據以往的開發經驗,我們最終將《大唐豪俠》的設計容量定爲5000人,即遊戲允許同時在線的玩家上限爲5000人。 明確了基本目標,程序開發人員將要面對多個挑戰,其中最主要的兩個是:

1. 如何表現即時戰鬥效果? 
2. 如果支撐5000玩家? 

前者體現在畫面效果和戰鬥節奏上,主要與渲染有關,不屬於本文討論的重點。本文主要探討第二個問題,即架構上的考量。 

第二個問題之所以是一個挑戰,是因爲存在着這樣一種矛盾,即單服的運算能力不能滿足遊戲對運算能力的需求。 

相對於回合制遊戲,採用即時戰鬥機制的遊戲需要實時計算更多的數據,例如在回合制遊戲中,回合發招完成後纔開始計算傷害效果,而即時戰鬥類遊戲會在持續播放戰鬥動畫的過程中不斷產生計算需求,只要播放到關鍵幀就要計算傷害效果。這樣,即時戰鬥類遊戲計算傷害效果的間隔更短,頻率更高,而對CPU的運算能力提出了更高的要求。在最近幾年,計算機技術的發展很快,可以用日新月異來形容,雖然雙核、多核、多CPU的計算機正逐步取代單CPU的計算機,但仍尚未成爲市場主流,而讓單CPU配置的主流服務器支撐這麼多玩家即時戰鬥多少還是會有些力不從心。因此如何進行遊戲服務器集羣的架構,從而有效地利用目前主流機型爲《大唐豪俠》服務,就成爲了一個挑戰。 

在遊戲架構裏,遊戲的同步策略是很重要的部分,它關係到遊戲運營時對網絡帶寬的要求。同步策略越優秀,網絡帶寬的耗用就越少,遊戲過程中的流暢度也會越好。《大唐豪俠》的總監趙青先生曾撰文詳細闡述過同步策略方面的問題,所以本文就不在這裏重複了。本文着重探討服務器構架過程中,面對各種技術方案,我們是如何進行考慮和選擇的。 

技術選擇
從宏觀的層面看,我們主要考慮了以下幾個方面的問題。但總的原則相當清晰:“簡單就是美”,KISS(Keep It Simple,Stupid)。這一原則同時兼顧到了項目的進度和遠景目標。 

單服/多服
在當前主流服務器配置的情況下,要支持5000人同時在線,就只能選擇多服務器架構,單服務器無法勝任這樣的要求。事實上,分佈式的服務器架構還給我們帶來了易擴展的好處。只要我們在設計遊戲服務器時,讓遊戲服務進程(GAS,Game Server)做完全相同的事情,那麼就能很容易地加入新的服務器。新服務器的加入,既可以在一定程度上提高支持同時在線玩家的能力,也可以分擔其它服務器的負擔。 

多線程/多進程
通常,用多線程編程方式構建應用程序,是利用多核或多CPU計算機性能的主要方式。但用多線程方式開發遊戲服務器並不是非常適合。

衆所周知,成功的遊戲離不開豐富的玩法,玩法越豐富,越能吸引玩家,玩家也越不容易感到枯燥,但這樣一來,遊戲的邏輯也會變得更加複雜。此外,遊戲的輸入主要來自玩家,玩家使用物品或者使用技能都可看作是對系統的輸入。另外,由於存在着用戶之間的交互,因此一個玩家的行爲往往對其它玩家存在着影響。這些因素的存在,增加了BUG出現的機率。由於硬件和操作系統的原因,線程切換時間不完全一致,在這種情況下,要對某些BUG進行復現則相當困難。 

綜合這些分析,可以簡單地說,以多線程方式開發,既有開發方面的難度,也有維護方面的難度。相比而言,多進程方式既可以滿足多服務器結構的要求,在一定程度上還規避了某些多線程的問題。 

大容量/小容量
通常,我們會希望在玩家視野所及的地方越熱鬧越好,這樣,人氣就會很旺。那麼是不是在每個遊戲服務進程中,支持的玩家也是越多越好呢? 實際上並不完全如此。 所謂玩家至上,是指讓玩家在遊戲進行的過程中能終保持良好的遊戲感覺。我們知道對於複雜程序來說,完全消滅BUG幾乎是不可能的,遊戲服務進程會因爲各種BUG或其它各種原因而崩潰、宕機。一個遊戲進程中的玩家越多,一次宕機影響到的玩家數量也會越多。如果排除該問題需要很長的時間,而遊戲進程又連續崩潰,那麼遊戲玩家的耐心和熱情將會受到很大的打擊。 顯然,我們是無法完全避免BUG的,也無法完全避免宕機,但是我們可以努力降低這些問題帶來的負面影響,使受影響的玩家保持在一個較少的數量上。這樣看來,一個進程中容納的玩家數就不應該太多,從目前的主流服務器配置來評估,單個CPU支持1000人是比較合理的數量級。 

無縫地圖/多地圖
國外幾款遊戲大作的場景多是無縫拼接起來的,以《魔獸世界》爲例,當玩家從西大陸進入東大陸時,會經過一個狹長的穀道,通過穀道需要幾十秒時間,客戶端有充足的時間在玩家行走過程中加載目的地的地圖信息。沒有了顯式地切換場景,玩家的感受可能會更連貫,但是在技術實現上,無縫地圖方式還有一些其它方面的因素要考慮,它是一個綜合問題,實現起來會比較複雜。

而在多地圖方式下,從一個場景到另外一個場景,要通過NPC對話或者經過TRAP點顯式控制場景切換,例如玩家如果想從新手村到長安,就要和驛卒對話,選擇要去的目的地,玩家選擇長安後,在客戶端,通常會顯示重新加載地圖文件的進度條,根據地圖文件的大小和實現技術的不同,加載一般需要幾秒到十幾秒不等。 
雖然多地圖方式比無縫地圖方式在表現上要稍差一些,但多地圖方式的技術實現相對簡單,同時,也會簡化和規避一些其它的技術問題。例如如果是無縫地圖方式,開發使用的地圖編輯器就需要能支持多人同時修改同一個地圖文件。 

動態/靜態負載均衡
在分佈式架構中,負載均衡是必須要面對和考慮的問題。 某些場景或者場景中的某個區域,可能會因爲多種因素的綜合效應聚合起相當數量的玩家,例如在門派入口處,玩家會自發的在這裏擺攤交易,形成商業區;又如戰場中的***要塞,是攻守雙方爭奪的焦點,也是玩家聚集的地方。如何才能保證在這種玩家聚集的情況下,仍然能讓他們感覺流暢,玩得暢快呢?這就需要考慮服務器的性能上限,調整服務器支持的人數,進行負載均衡。 

負載均衡主要有兩種策略:動態負載均衡和靜態負載均衡。Big World引擎(網易《天下II》採用)採用動態負載均衡,它把遊戲世界(無縫地圖方式)分爲多個區域,每個服務器承載其中的幾個區域,當某個區域負載較高時,就將該區域細分爲幾個更小的區域,並把其中某些區域的數據遷移到其它服務器上,讓其它服務器來承載。但是動態負載均衡對架構設計要求較高,還要考慮如何確保在數據轉移過程中,玩家及其它相關對象狀態的遷移、組隊信息的更新等等能夠保持正確,其中的工作量不小。

《大唐豪俠》採用靜態負載均衡,我們把遊戲世界分成多個場景,每個遊戲服務器承載其中的幾個場景,遊戲場景的大小在設計時就已經確定下來了,不會在運營期間再進行動態調整。服務器的負載可以在遊戲服務器啓動前進行評估,評估時要考慮的因素主要包括地圖是否是玩家特別喜歡的場景、地圖是否經常被運營商選取進行線上活動以及過去的運營情況等。這樣,在每週例行維護時,我們就能評估出每張地圖的人數,從而估算出它們的負載,比較合理地把它們分配到不同的服務器上。雖然靜態負載均衡比動態負載均衡缺少靈活性,但是實際效果也是相當不錯的。

C++/腳本
採用即時戰鬥機制的遊戲有一定程度的密集運算,因此,我們在開發過程中,一直在注意避免因運行效率導致設計目標無法達成的問題。計算量較大的部分主要是每幀對象的狀態更新、尋路和戰鬥的效果計算,我們選擇用C++實現這些功能讓運行效率最大化,而其它非密集運算的功能,如普通的送信任務、找人任務等遊戲玩法,則可用腳本方法來實現。 至於選擇哪種腳本語言,則是仁者見仁、智者見智了,在開發《大唐豪俠》時,我們更熟悉Python,並且這門語言也有非常多的優點(除了速度慢了點以外),因此就選用了它。 

界定何時該用腳本,何時該用C++有一個簡單的標準,即改動不頻繁,計算量大的應該用C++;改動頻繁,計算量小的應該用腳本。使用C++,爲了提高運行效率,會有較多的時間花在編譯上;而腳本語言則犧牲了運行效率,獲得了運行期間動態變化的能力。我們知道,遊戲開發是一個不斷響應快速變化的過程。如果頻繁更新的部分也用C++來實現,則會顯著的增加編譯時間,開發的效率會很低。 

雖然此處將這些因素分開列舉,但實際上,這些問題並非獨立存在,它們之間也會產生相互影響。例如多服務器架構是多進程間的協作,多進程的協作可自然延伸爲多服務器架構。又如多場景地圖和靜態負載平衡都在一定程度上降低了開發的難度,多場景地圖爲服務器的靜態負載平衡提供了條件。 

設計要考慮的當然遠不止上面談到的這些內容,設計既是系統化的工作,也是一個尋求平衡的過程,這裏只是站在宏觀的層面,羅列了較爲重要的部分,而這些部分就決定了遊戲的架構最終會是什麼樣子。 

遊戲架構
基於上面的分析,服務器架構就變得很清楚了。圖01簡單的描述了架構中的服務器以及它們之間是如何進行交互的,虛線表示服務器間通過短連接交換信息,實線表示服務器間建立了長連接。爲簡單起見,這裏只畫出了2個GAS,實際上一共有5個GAS。 

其中,GAC是遊戲客戶端(Game Client),其它是服務器,這些服務器的職責分別是: 

GCC(Game Cluster Controler)
GCC是遊戲服務集羣(Cluster)的核心,是大腦,只有GCC才能決定集羣級別信息的準確性和唯一性。 
GCC肩負登錄服務器的功能,玩家通過遊戲客戶端輸入用戶名和密碼,隨後用戶名和密碼被髮送到GCC,由GCC轉發給認證服務器,通過認證的玩家信息會通過DBS從數據庫中取出,並在GCC上判斷玩家應該連接的GAS,接着登錄成功協議和重連協議被髮送到客戶端,客戶端按照重連協議的指示連接到指定的GAS並進入指定的遊戲場景。 

GCC也是遊戲世界中的位置寄存器,保存着所有在線玩家的位置信息。任何涉及跨GAS玩家的信息交換和事務處理,或者交給GCC處理,或者向GCC查詢目標玩家的位置後由GAS直接與目標GAS協商。 

DBS(Database Server)
DBS負責把遊戲世界中的持久化數據存儲到存儲系統中,DBS把GAS/GCC與存儲系統隔離開來,GAS和GCC完全不需要知道後臺的存儲系統是什麼,數據是如何被存儲的是由DBS來決定,它即可以存爲文件,也可以存到數據庫中。《大唐豪俠》的後臺存儲採用MySQL數據庫。 
鑑於DBS能直接訪問後臺的存儲系統,非常重要和敏感,因此DBS只接收來自GAS和GCC的鏈接,並放在防火牆後面保護起來。DBS把GAS/GCC發來的協議轉換爲sql語句丟給MySQL服務器執行。DBS也會負責一部分的遊戲邏輯,如離線玩家數據的處理;同時還會分析請求的特點,區分出請求能否並行執行,把能並行執行的並行處理,不能並行執行的就串行處理。 

GAS(Game Server)
GAS接收客戶端發來的協議,過濾非法數據包,並分析協議中的內容。例如分析玩家是要使用道具還是要使用技能,然後按照遊戲玩法規則,根據玩家的行爲完成遊戲邏輯的計算。遊戲的大部分玩法都在GAS上處理,只有少量的全局性邏輯或跨服務器的玩法會由GCC處理,離線玩法則主要由DBS處理。 

在這些服務器中,GCC是大腦,所以如果GCC崩潰,遊戲服務集羣狀態的一致性就無法保證,遊戲就得立即停下來。而如果DBS崩潰,用戶的數據無法讀取,玩家就無法登錄,也無法保存玩家數據,這樣可能會導致玩家數據丟失,所以遊戲也得立即停下來。相對而言,如果GAS崩潰,問題就不會有這麼嚴重,主要隻影響到該服務器上在線的玩家,我們可以重啓GAS並將其重新加入集羣中,GAS就能繼續提供服務了。 

爲簡單起見,我們沒有設計恢復處理,而是選擇了一種簡單的處理方案:當GCC和DBS崩潰時,集羣關閉;GAS崩潰時,自動重啓重新加入集羣。從運營效果看,這種方案不但能讓我們快速發現問題,找到問題,避免了爲實現容錯而增加的複雜性,也避免了數據不一致性對遊戲系統和玩家帶來的損失。 

總結
隨着互聯網的發展,越來越多的人享受網絡生活,MMOG是其中最具吸引力的娛樂方式,玩家也不僅僅滿足於小範圍內的相互交流,他們期望能夠與來自不同地域更多的玩家一起在遊戲中探索、相遇、結識,因此,如何在一個遊戲世界支持更多的玩家,就成爲MMOG遊戲開發的一個挑戰和難題。分佈式/集羣是對該問題行之有效的解決方案,雖然在這種方案的實現過程中也會面臨着許多挑戰,但越來越多的遊戲,根據自身的特點找到了合適的平衡點,它們的解決方案也一再地在實踐中得到證實。本文站在《大唐豪俠》開發的角度,分析和探討適合《大唐豪俠》的遊戲框架,希望這種框架對其它遊戲的設計也將有所啓發。

 

 

PS: 最近搞遊戲開發,轉載過來,瞭解一下!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章