-
1 # 卡吃地瓜
-
2 # 小毛同學不想上班
遊戲伺服器特徵
短連線遊戲伺服器架構
長連結遊戲伺服器架構
分割槽分服伺服器架構
MMOARPG伺服器架構
房間伺服器架構
一、遊戲伺服器特徵
遊戲伺服器端,是一個會長期執行的程式,並且它還要服務於多個不定時,不定點的網路請求。所以這類軟體的特點是要非常關注穩定性和效能。這類程式如果需要多個協作來提高承載能力,則還要關注部署和擴容的便利性;同時,還需要考慮如何實現某種程度容災需求。由於多程序協同工作,也帶來了開發的複雜度,這也是需要關注的問題。
功能約束,是架構設計決定性因素。基於遊戲領域的功能特徵,對伺服器端系統來說,有以下幾個特殊的需求:
對於遊戲資料和玩家資料的儲存
對玩家資料進行資料廣播和同步
把一部分遊戲邏輯在伺服器上運算,做好驗證,防止外掛。
針對以上的需求特徵,在伺服器端,我們往往會關注對電腦記憶體和CPU的使用,以求在特定業務程式碼下,能儘量滿足承載量和響應延遲的需求。最基本的做法就是“空間換時間”,用各種快取的方式來以求得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》程式的原始碼在 ARPANET共享之後,在全世界廣泛流行起來。不斷完善的 MUD1的基礎上產生了開源的 MudOS(1991),成為眾多網遊的鼻祖。
MUD1 是一款純文字的世界,沒有任何圖片,但是不同計算機前的玩家可以在遊戲裡共同冒險、交流。
與以往具有網路聯機功能的遊戲相比, MUD1是第一款真正意義上的實時多人互動的網路遊戲,它最大的特色是能夠保證整個虛擬世界和玩家角色的持續發展——無論是玩家退出後重新登入還是伺服器重啟,遊戲中的場景、寶箱、怪物和謎題仍保持不變,玩家的角色也依然是上次的狀態。
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個問題:
多個地圖節點如何無縫拼接,特別是當地圖節點比較多的時候,如何保證無縫拼接
如何支援動態分佈,有些區域人多,有些區域人少,保證伺服器資源利用的最大化
為了解決這個問題,比較以往按照地圖
回覆列表
一、早期網遊伺服器。
蠻荒時期的遊戲伺服器框架我們一筆帶過,那時的遊戲伺服器和一個小Web服務沒有區別。
從《傳奇》的時代開始,遊戲伺服器就不再是簡單的上傳存檔、下載存檔、訪問頁面而已。遊戲伺服器內部出現了遊戲邏輯,既能用於同步每個玩家看到的世界,又能讓邏輯與客戶端分離,避免早期的網路遊戲那種毫無防範的邏輯體系(對外掛防禦能力為0)。
如圖,客戶端透過某種形式驗證登陸以後,就和伺服器透過TCP直接相連了。這種伺服器的承載能力不高,但那時在遊戲邏輯上也務求簡化,把負載減少到極致。
· 例如:1、玩家看不到怪物的血量,或者只能看到正在打的怪物的血量。2、地圖有格子的概念,每個格子只能有一個單位,極大限制了同屏人數。
由於邏輯儘量簡化,雖然這時的伺服器邏輯服務都是單程序單執行緒的,但是也足夠表現互動的感受。
這種架構奇怪的地方是處理網路連線資料傳輸的壓力和邏輯處理的壓力在同一個伺服器上(儲存模組可能也在同一個程序),就算邏輯處理壓力為0,承載人數也高不到哪去。
雖然這時的遊戲伺服器設計很簡陋,但是網遊第一次給了玩家真實世界的感受。單服人數不足的問題可以靠開多組伺服器實現,所以曾經出現了幾百上千組伺服器的輝煌時代。
二、早期遊戲伺服器的改進版本當開發者們有了初步經驗以後,新作品的開發,自然而然的過渡到了如下的形式:
遊戲邏輯服務依然是在一臺伺服器上,單程序(邏輯處理本身肯定是在一個執行緒中,可以有子執行緒負責內網通訊)。但是我們自然的想到,儲存負載和網路連線負載可以從邏輯服上拆出來。
連線伺服器負責把客戶端和伺服器之間的訊息轉化為伺服器之間的訊息,可以順便做一些加解密的工作。
這一點小改動極大提高了單服連線人數的上限。但是玩家要求提高了,空出來的效能很快被豐富的遊戲系統吃掉了。
由於連線伺服器本身沒有時序性,很容易做分散式的(其實大部分遊戲還是隻用一個連線服),儲存服務不要求高實時性,高峰期存檔間隔可以稍長一些,不會對遊戲服造成影響。
三、成熟形態的伺服器框架邏輯伺服器的負載均攤方法一:按照功能劃分多個伺服器程序
邏輯伺服器的負載均攤方法二:按照場景劃分多個伺服器程序
對遊戲伺服器歷史有了基本瞭解後,成熟形態的遊戲伺服器很容易理解。簡單來說,就是把邏輯伺服器單個程序的壓力分攤到多個伺服器。
難點在邏輯的設計上,要像做手術一樣把本來是一體的功能切開,並抽象出若干個API來保持聯絡(伺服器之間是TCP連線)。
在分解時,要找聯絡相對最薄弱的環節入手,比如場景和場景之間分開、單獨抽出聊天服務、組隊服務、好友服務。
無論如何分解,最終結果只能是有限個服務。而且分解的越細,開發難度就越大。因為跨伺服器邏輯是把簡單的同步邏輯變成了非同步Callback邏輯,而且容易出現時序問題等不易測試的問題。
單個場景服務幾乎是無法分解的。分解單個場景難度巨大以至於出現了BigWorld引擎來專門的解決場景分割問題,後面會談到。
這種成熟形態的遊戲伺服器已經能滿足現實中99%的頻繁互動類網遊需求,是大型MMO端遊、頁遊的主流形式。
當然有實力的公司在這個基礎上會做很多改動,實現動態開闢副本、相位技術等等,但是萬變不離其宗,其本質和上圖沒有什麼區別。
附:開房間式的網路遊戲
開房間式的網路遊戲也是遊戲的一個重要分支,英雄聯盟、DOTA、很多手遊例如皇室戰爭、王者榮耀等等。
這種遊戲房間之間幾乎沒有互動,只有大廳內有互動,可以理解為原始形態的遊戲伺服器的平行擴充套件。
房間式遊戲擴充套件難度較小,只是需要根據玩家數量動態擴充套件遊戲房間的數量、伺服器數量。很像網站的架構。
只是,畢竟遊戲不都是開房間的玩法。
小結:遊戲伺服器框架特點
1、真正的資料都在記憶體中,資料庫效能不那麼重要
· 注:很多大型遊戲採用了共享記憶體,避免宕機時損失過大。
2、單CPU效能比CPU數量重要的多。
3、目前有很多遊戲,特別是手遊,使用Redis讀寫代替記憶體讀寫,甚至也有用Mongo的。
4、開新服、舊區合服的情況,非常適合雲平臺。
五、先進伺服器框架
· 先進伺服器框架1 BigWorld
BigWorld引擎的代表作:
· 中國:《天下貳 》《天下叄》等等數十款,網易對BigWorld的實用化貢獻很大。
· 國際:《魔獸世界》早期版本,《坦克世界》,《戰爭雷霆》
BigWorld的核心理念,要回到上面講過的場景分割問題。
BigWorld利用平面切分的原理,將場景劃分為小塊,不同的塊可以執行在不同的伺服器上。而且塊的劃分是動態的,根據玩家密集程度、數量動態調整塊的大小。。
具體技術上,使用了Actor模型,要求每個物件都是獨立的Entity,物件之間只能透過訊息協作,嚴格限制物件之間的直接互動。
後來隨著手遊的崛起、端遊的衰落、網遊玩法向多元化發展,這一系列的變化,導致BigWorld引擎很快就衰落了。
BigWorld引擎從曾經的大紅大紫,到現在的無人問津,反映出遊戲伺服器技術的發展趨勢。BigWorld的強制Actor模型,實際上是犧牲了開發效率,換取了伺服器可擴充套件性。
理論上單服承載人數可以達到百萬級別。但是遊戲的業務邏輯的修改很頻繁,開發效率低下是遊戲設計師不能承受之重。
這種架構天生就是為雲計算準備的,而且單個物理機承載量十分有限,每個遊戲大區都需要大量實體機。
如果BigWorld成功…… 可惜的是,它和實際市場的發展趨勢背道而馳了。
遊戲開發相比電商系統,專案規模小几個數量級,但是相對的,迭代速度要快幾倍。專案之間如果型別不同或是玩法有差異,能複用的程式碼並不多。
聊聊十萬行程式碼。遊戲伺服器開發速度受美術資源製作速度、客戶端開發速度制約。近幾年我猜測伺服器方面並不會有大的技術革新。
遊戲開發未來的趨勢是多元化、低門檻化、大眾化。很長一段時間內BigWorld這種大怪獸級別的引擎不會再崛起。
分散式框架的崛起時間點,無論如何,也在VR技術成熟之後了。
· 先進伺服器框架2、Skynet
Skynet是新興的一種通用型伺服器框架(完全開源),它遊走在傳統不易分佈伺服器和分散式伺服器之間。
它是一種泛用型框架,不僅能很好的作為遊戲伺服器框架使用,而且用來搭建HTTP服務也具有驚人的效能(幾百行程式碼的簡單HTTP實現,能達到nginx 60%的效能)。
矛盾的是,由於它對指令碼虛擬機器做了一些重要的Hack,導致它完全繫結在了Lua這一種語言上。
Skynet原理闡述:
把服務抽象為微服務,一個系統內可以建立成千上萬個微服務,Skynet排程m個執行緒(m=CPU核心數)、處理n個微服務各自的事件。
由於n個微服務在同一個程序內,可以達到0延遲的內部通訊(極端情況下無複製)
同時Lua虛擬機器又提供了沙盒機制,微服務之間的Lua邏輯程式碼不會有任何干擾,必要的時候又可以在C語言層面、Lua沙盒之外共享資料。
由於服務本身有良好的隔離性,可以較為方便的把服務部署到多物理機上(考慮到效能問題,不能像BigWorld那樣任意部署)。
Skynet這種架構已經在Lua體系的遊戲公司內大量使用(以網易係為代表),悄無聲息的滲透到其他公司裡。(和Lua語言當年的情況有點像,是金子總會發光的。)
Go語言的goroutine特性,給遊戲開發者帶來巨大的想象空間。
在Go語言的基礎上,很容易出現更好的房間式遊戲框架、類似Skynet的框架、改進型的傳統框架。
但是可以大膽預測,最終實現的效果不會超過erlang、skynet這類框架的範圍。這是因為遊戲業務本身的特性決定的。
結束語
本文簡要探討了十幾二十年來,主流伺服器框架的發展脈絡,以MMO-RPG這種最具代表性的網遊型別為主(同時MMO對伺服器架構的挑戰也是最大的),兼談到一些其他型別的遊戲。由於遊戲型別多種多樣,各個國家和地區的開發商所偏好的架構方式也大有不同,文中難免掛一漏萬,但不太影響整體脈絡,也不影響對網遊伺服器的核心問題的總結——邏輯拆分。