遊戲服務器架構演進(完整版)
一、遊戲服務器特征
遊戲服務器,是一個會長期運行程序,並且它還要服務於多個不定時,不定點的網絡請求。所以這類服務的特點是要特別關注穩定性和性能。這類程序如果需要多個協作來提高承載能力,則還要關注部署和擴容的便利性;同時,還需要考慮如何實現某種程度容災需求。由於多進程協同工作,也帶來了開發的複雜度,這也是需要關注的問題。
功能約束,是架構設計決定性因素。基於遊戲業務的功能特征,對服務器端係統來說,有以下幾個特殊的需求:
- 遊戲和玩家的數據存儲落地
- 對玩家交互數據進行廣播和同步
- 重要邏輯要在服務器上運算,做好驗證,防止外掛。
針對以上的需求特征,在服務器端,我們往往會關注對電腦內存和CPU的使用,以求在特定業務代碼下,能盡量滿足高承載低響應延遲的需求。最基本的做法就是“空間換時間”,用各種緩存的方式來以求得CPU和內存空間上的平衡。另外還有一個約束:帶寬。網絡帶寬直接限製了服務器的處理能力,所以遊戲服務器架構也必定要考慮這個因素。
二、遊戲服務器架構要素
對於遊戲服務端架構,最重要的三個部分就是,如何使用CPU、內存、網卡的設計:
- 內存架構:主要決定服務器如何使用內存,以最大化利用服務器端內存來提高承載量,降低服務延遲。
- 邏輯架構:設計如何使用進程、線程、協程這些對於CPU調度的方案。選擇同步、異步等不同的編程模型,以提高服務器的穩定性和承載量。可以分區分服,也可以采用世界服的方式,將相同功能模塊劃分到不同的服務器來處理。
- 通信模式:決定使用何種方式通訊。基於遊戲類型不同采用不同的通信模式,比如http,tcp,udp等。
三、服務器演化進程
1、卡牌等休閑遊戲弱交互遊戲
服務器基於遊戲類型不同,所采用的架構也有所不同,我們先講一下簡單的模型,采用http通信模式架構的服務器:
這種服務器架構和我們常用的web服務器架構差不多,也是采用nginx負載集群支持服務器的水平擴展,memcache做緩存。唯一不同的地點不同的在於通信層需要對協議再加工和加密,一般每個公司都有自己的一套基於http的協議層框架,很少采用開源框架。
2、長鏈接遊戲服務器
長連接遊戲和弱聯網遊戲不同的地方在於,長連接中,玩家是有狀態的,服務器可以時時和client交互,數據的傳送,不像弱聯網一般每次都需要重新創建一個連接,消息傳送的頻率以及速度上都快於弱聯網遊戲。長鏈接網遊的架構經過幾代的迭代,類型也變得日益豐富,以下為每一代服務器的特點以及架構模式。
1)、第一代網遊服務器(單線程無阻塞)
最早的遊戲服務器是1978年,英國著名的財經學校University of Essex的學生 Roy Trubshaw編寫了世界上第一個MUD程序,叫做《MUD1》。
MUD1 是一款純文字的世界,沒有任何圖片,但是不同計算機前的玩家可以在遊戲裏共同冒險、交流。與以往具有網絡聯機功能的遊戲相比, MUD1是第一款真正意義上的實時多人交互的網絡遊戲,它最大的特色是能夠保證整個虛擬世界和玩家角色的持續發展——無論是玩家退出後重新登錄還是服務器重啟,遊戲中的場景、寶箱、怪物和謎題仍保持不變,玩家的角色也依然是上次的狀態。
MUD中文版
MUDOS使用單線程無阻塞套接字來服務所有玩家,所有玩家的請求都發到同一個線程去處理,主線程每隔1秒鍾更新一次所有對象(網絡收發,對象狀態,刷新地圖,刷新NPC)。用戶使用 Telnet之類的客戶端用 Tcp協議連接到 MUDOS上,使用純文字進行遊戲,每條指令用回車進行分割。這樣的係統在當時每台服務器承載個4000人同時遊戲。從1991年的 MUDOS發布後,全球各地都在為他改進,擴充,推出新版本。
MUDOS中遊戲內容通過 LPC腳本進行定製,邏輯處理采用單線程tick輪詢,這也是第一款服務端架構模型,後來被應用到不同遊戲上。後續很多遊戲都是跟《UO》一樣,直接在 MUDOS上進行二次開發,直到 如今,一些回合製遊戲,以及對運算量小的遊戲,依然采用這種服務器架構。
第一代服務器架構圖:
線程模型
2) 、第二代網遊服務器(分區分服)
2000年左右,隨著圖形界麵的出現,遊戲更多的采用圖形界麵與用戶交互。此時隨著在線人數的增加和遊戲數據的增加,服務器變得不抗重負。於是就有了分服模型。分服模型結構如下:
分服模型是遊戲服務器中最典型,也是曆久最悠久的模型。在早期服務器的承載量達到上限的時候,遊戲開發者就通過架設更多的服務器來解決。這樣提供了很多個遊戲的“平行世界”,讓遊戲中的人人之間的比較,產生了更多的空間。其特征是遊戲服務器是一個個單獨的世界。每個服務器的帳號是獨立的,每台服務器用戶的狀態都是不一樣的,一個服就是一個世界,大家各不牽扯。
後來遊戲玩家唿籲要跨服打架,於是就出現了跨服戰,再加上隨著遊戲的運行,單個服務器的遊戲活躍玩家越來越少,所以後期就有了服務器的合並以及遷移,慢慢的以服務器的開放、合並形成了一套成熟的運營手段。目前多數遊戲還采用分服的結構來架設服務器,多數頁遊還是采用這種模式。
線程調度
分服雖然可以解決服務器擴展的瓶頸,但單台服務器在以前單線程的方式來運行,沒辦法充分利用服務器資源,於是又演變出了以下2種線程模型。
- 異步-多線程,基於每個場景(或者房間),分配一個線程。每個場景的玩家同屬於一個線程。遊戲的場景是固定的,不會很多,如此線程的數量可以保證不會不斷增大。每個場景線程,同樣采用tick輪詢的方式,來定時更新該場景內的(對象狀態,刷新地圖,刷新NPC)數據狀態。玩家如果跨場景的話,就采用投遞和通知的方式,告知兩個場景線程,以此更新兩個場景的玩家數據。
- 多進程。由於單進程架構下,總會存在承載量的極限,越是複雜的遊戲,其單進程承載量就越低,因此一定要突破進程的限製,才能支撐更複雜的遊戲。多進程係統的其他一些好處:能夠利用上多核CPU能力、更容易進行容災處理。
多進程係統比較經典的模型是“三層架構”,比如,基於之前的場景線程再做改進,把網絡部分和數據庫部分分離為單獨的進程來處理,邏輯進程專心處理邏輯任務,不合IO打交道,網絡IO和磁盤IO分別交由網路進程和DB進程處理。
3)、第三代網遊服務器
之前的網遊服務器都是分區分服,玩家都被劃分在不同的服務器上,每台服務器運行的邏輯相同,玩家不能在不同服務器之間交互。想要更多的玩家在同一世界,保持玩家的活躍度,於是就有了世界服模型了。世界服類型也有以下3種演化:
一類型(三層架構)
網關部分分離成單端的gate服務器,DB部分分離為DB服務器,把網絡功能單獨提取出來,讓用戶統一去連接一個網關服務器,再有網關服務器轉發數據到後端遊戲服務器。而遊戲服務器之間數據交換也統一連接到網管進行交換。所有有DB交互的,都連接到DB服務器來代理處理。
二類型(cluster)
有了一類型的經驗,後續肯定是拆分的越細,性能越好,就類似現在微服務,每個相同的模塊分布到一台服務器處理,多組服務器集群共同組成一個遊戲服務端。一般地,我們可以將一個組內的服務器簡單地分成兩類:場景相關的(如:行走、戰鬥等)以及場景不相關的(如:公會聊天、不受區域限製的貿易等)。經常可以見到的一種方案是:gate服務器、場景服務器、非場景服務器、聊天管理器、AI服務器以及數據庫代理服務器。如下模型:
以上中我們簡單的講下常見服務器的三種類型功能:
- 場景服務器:它負責完成主要的遊戲邏輯,這些邏輯包括:角色在遊戲場景中的進入與退出、角色的行走與跑動、角色戰鬥(包括打怪)、任務的認領等。場景服務器設計的好壞是整個遊戲世界服務器性能差異的主要體現,它的設計難度不僅僅在於通信模型方麵,更主要的是整個服務器的體係架構和同步機製的設計。
- 非場景服務器:它主要負責完成與遊戲場景不相關的遊戲邏輯,這些邏輯不依靠遊戲的地圖係統也能正常進行,比如公會聊天或世界聊天,之所以把它從場景服務器中獨立出來,是為了節省場景服務器的CPU和帶寬資源,讓場景服務器能夠盡可能快地處理那些對遊戲流暢性影響較大的遊戲邏輯。
- 網關服務器: 在類型一種的架構中,玩家在多個地圖跳轉或者場景切換的時候采用跳轉的模式,以此進行跳轉不同的服務器。還有一種方式是把這些服務器的節點都通過網關服務器管理,玩家和網關服務器交互,每個場景或者服務器切換的時候,也有網關服務器統一來交換數據,如此玩家操作會比較流暢。
通過這種類型服務器架構,因為壓力分散了,性能會有明顯提升,負載也更大了,包括目前一些大型的 MMORPG遊戲就是采用此架構。不過每增加一級服務器,狀態機複雜度可能會翻倍,導致研發和找bug的成本上升,這個對開發組挑戰比較大,沒有經驗,很容出錯。
三類型(無縫地圖)
魔獸世界的中無縫地圖,想必大家印象深刻,整個世界的移動沒有像以往的遊戲一樣,在切換場景的時候需要loading等待,而是直接行走過去,體驗流暢。
現在的遊戲大地圖采用無縫地圖多數采用的是9宮格的樣式來處理,由於地圖沒有魔獸世紀那麼大,所以采用單台服務器多進程處理即可,不過類似魔獸世界這種大世界地圖,必須考慮2個問題:
- 多個地圖節點如何無縫拚接,特別是當地圖節點比較多的時候,如何保證無縫拚接
- 如何支持動態分布,有些區域人多,有些區域人少,保證服務器資源利用的最大化
為了解決這個問題,比較以往按照地圖來切割遊戲而言,無縫世界並不存在一塊地圖上麵的人有且隻由一台服務器處理了,此時需要一組服務器來處理,每台 Node服務器用來管理一塊地圖區域,由 NodeMaster(NM)來為他們提供總體管理。更高層次的 World則提供大陸級別的管理服務。
一個 Node所負責的區域,地理上沒必要連接在一起,可以統一交給一個Node去管理,而這些區塊在地理上並沒有聯係在一起的必要性。一個 Node到底管理哪些區塊,可以根據遊戲實時運行的負載情況,定時維護的時候進行更改 NodeMaster 上麵的配置。
對象的無縫遷移
玩家A、B、C分別代表3種不同的狀態,以及不同的遷移方式,我們分別來看。
- 玩家A: 玩家A在node1地圖服務器上,由node1控製,如果遷移到node2上,需要將其數據複製到node2上,然後從node1移除。
- 玩家B: 玩家B在node1和node2中間,此時由node1和node2維護,若是從node1行走到node2的過程中,會向1請求,同時向2請求,待全部移動過去了再移除。
- 玩家C:玩家C在node2地圖服務器上,由node2控製,如果遷移到node1上,需要將其數據複製到node1上,然後從node2移除。
具體魔獸世界服務器的分析,篇幅過多,我們以後再聊。
3、房間服務器(遊戲大廳)
房間類玩法和MMORPG有很大的不同,在於其在線廣播單元的不確定性和廣播數量很小。而且需要匹配一台房間服務器讓少數人進入一個服務器。
這一類遊戲最重要的是其“遊戲大廳”的承載量,每個“遊戲房間”受邏輯所限,需要維持和廣播的玩家數據是有限的,但是“遊戲大廳”需要維持相當高的在線用戶數,所以一般來說,這種遊戲還是需要做“分服”的。典型的遊戲就是《英雄聯盟》這一類遊戲了。而“遊戲大廳”裏麵最有挑戰性的任務,就是“自動匹配”玩家進入一個“遊戲房間”,這需要對所有在線玩家做搜索和過濾。
玩家先登錄“大廳服務器”,然後選擇組隊遊戲的功能,服務器會通知參與的所有遊戲客戶端,新開一條連接到房間服務器上,這樣所有參與的用戶就能在房間服務器裏進行遊戲交互了。
四、最後
遊戲行業相對於互聯網應用來說,其開放性和標準化並不完善,這就導致了很其他行業看遊戲有一種神秘麵紗,隱秘而封閉。
造成這個原因有很多,遊戲業務的複雜性以及受眾群體小是主要原因,它不像web應用天生有開源組織和社區基因的支持,也沒有互聯網行業的如此大的受眾麵和影響力,除了一些比較出名的遊戲引擎以外其他的功能組建都是有各個遊戲公司基於自己業務邏輯自己搭建,每個公司業務方向不同又加大了知識的流通以及標準的建立,這對整個生態的發展已經產生了製約,特別是那些想加入遊戲行業的新人來說,準入門檻較高,網上可找到的學習資料也很少。
這種現象目前正在發生改變,除了受眾群體越來越大和豐富以外,還有一些技術組織正在推進整個社區的進步。
比如每年一度的unity 技術大會,以及其他優秀的開源引擎都在積極推進整個遊戲社區的創建,除了吸引更多優秀的技術人才和團隊加入,這一切都讓遊戲行業變得越來越開放和規範,讓行業內的知識也得以流通和繼承。當然了,也期望每個遊戲人能夠加入進來,分享自己的知識,讓自由開放的共享精神傳承每個地方。
本文作者:佚名
來源:51CTO
最後更新:2017-11-02 11:34:05