本文所討論的只是現有網遊中一部分會採用這種架構思想,但自己,網遊種類不少,因此須要專門對當前網遊的需求進行一系列改變,因此本文不表明全部網遊服務器架構。算法
對於網遊的服務器架構設計,咱們主要考慮下列幾個點:數據庫
1,遊戲類型:現有的遊戲類型有不少,有房間式、世界類型、場景型(跟房間類型有部分相同)設計模式
2,鏈接方式:大部分遊戲採用TCP長鏈接方式,但有些遊戲也採用UDP傳輸或TCP/UDP共用緩存
3,在線人數:爲了保證遊戲的流暢性,咱們必須確保服務器集羣的容災和擴容性,以便於防止同時在線人數的增長。安全
4,性能方面:主要考慮內存、CPU、寬帶等物理問題。服務器
本文主要說明一類場景型網遊服務器架構的設計框架。網絡
首先咱們理解一個網遊服務器在遊戲中所扮演的角色,咱們能夠把它理解爲一個上帝角色,它擁有當前場景下全部玩家、NPC、事物對象的各種屬性(狀態、動做)的一個讀寫權,它能夠經過自身的邏輯處理,將多個客戶端鏈接到一塊兒,呈現一個「統一」的世界。多線程
下面先介紹一款遊戲(刀劍Online,參考了網上其餘大神的Blog)的服務器架構:架構
1,鏈接負載服務器(Connection Load Server - CLS)負載均衡
CLS主要負責服務器端與客戶端的網絡鏈接,主要做用就是將真正的服務器端口與客戶端隔離開,CLS進行網絡鏈接的處理,信息交互的處理,錯誤數據的處理......,有了CLS層後,咱們能夠有效的下降了邏輯層的負擔,同時保證邏輯層對於客戶端徹底是一個黑盒服務器,確保其安全性,還有即是TCP長連問題,咱們不須要再對TCP的鏈接進行處理,若是更換場景時,僅須要服務器端進行一個場景服務器的切換而已,TCP端口並不須要斷開重連。通常咱們只在CLS層進行網絡數據的交互,關於遊戲邏輯層的處理,是徹底交給下層服務器處理,主要是爲了保證框架的理解性,使得整個框架更加清晰。
在編寫CLS層的代碼時,咱們須要注意一些細節:
⑴,對於遊戲服務器的邏輯處理,咱們爲了保證遊戲的流暢性,就必須保證網絡通信作到及時處理,而TCP協議中,默認會開啓Nagle算法,此算法的設計理念是在早期,若不斷進行一個字節數據包的傳輸,TCP傳輸的有效利用率就會大大下降,大部分的CPU資源會浪費在TCP傳輸包的處理上,對於一些負載較重的網絡,很容易形成擁堵,而Nagle算法就是在發送端欲發送屢次少許數據包時,會先將第一個數據包發出去,其餘小的數據包會先進行緩存,只到第一個數據包的ACK返回時,纔會把緩存的包發出去,這樣能夠有效下降TCP傳輸中的資源浪費,但對於遊戲中,這種作法是很難接受的,因此咱們就須要關閉這個算法,在鏈接時使用TCP_NODELAY參數,禁用Nagle算法。
⑵,鏈接負載服務器組在與客戶端進行交互以前,還必須先進行網絡鏈接,若是僅僅只是一個1W人同時在線的小型遊戲,那基本不須要多考慮,直接一臺服務器進行鏈接交互就能夠,但若是人數過大,一臺服務器根本沒法負擔起這個工做,就須要一個服務器組,其前段再有一個負載均衡器對客戶端的鏈接進行分配就能夠
⑶,鏈接負載服務器對客戶端發過來的數據如何進行解密,現有兩種作法,一種是部分解密,一種是徹底解密,具體參照上述說CLS層最好不要作過多其餘層的東西,那一樣邏輯層最好接受到的信息,也是不須要解密的明文類型,因此我的建議是在CLS層對數據包進行徹底解密,再傳輸給邏輯層進行處理。
2,總控服務器(Master Server - MS)
總控服務器有管理當前全部遊戲相關內容的全向,好比某個客戶端的登陸、註銷、角色操做、不一樣場景間的互動均由總控服務器處理,MS須要與其餘的場景服務器時刻保持鏈接,咱們能夠把MS想象成一個服務器的中樞,它先從CLS層取得數據包,而後根據是否須要場景服務器處理判斷後,若是不須要,自身處理完將結果反饋給CLS,若是須要,則分配給專門的某個場景服務器進行處理,並通知CLS層,讓後續該用戶的指令均直接轉給該場景服務器處理。以其工做場景舉個例子:
⑴,用戶A與B分別處於不一樣場景服務器的管理上,A與B溝通時,則須要A的場景服務器將信息包發送給MS,包中有用戶B的基礎信息,MS會在臨時管理信息庫中進行檢索,找到B的信息後,將該信息包再次發送給B所屬的場景服務器。
⑵,用戶A處於場景服務器1中的傳送點,要傳送到場景服務器2管理的場景,則會發送指令給MS,由MS給場景服務器2發送接收指令,這時便有MS對用戶A進行服務器信息的更換,將用戶A的信息處理放置在場景服務器2中。
3,場景服務器(Zone Server - ZS)
場景服務器主要負責當前場景下的全部玩家的指令交互和邏輯處理,通常一個場景服務器所能同時支撐的在線人數在10000人左右,若是超過就會嚴重影響玩家的使用,因此對於一個玩家很是多的遊戲,都會採用一種逆向管理髮,以玩家ip進行服務器的分配,而再也不是場景(固然,對於如今的場景型網遊,通常不多出現一個場景下萬人在線)。
對於場景服務器,通常必須的要求有:1,高效(解決方法就是儘量提高效率和代碼的優化,並使用高效的腳本語言)
2,災難回覆機制,當ZS發生非法操做致使影響某段時間用戶的數據,就必需要保證沒隔一段時間就對數據進行一次存儲,這樣的代價就是大你們中了對數據庫方面的負擔(但沒有辦法,必須的),而有些遊戲的作法就是將某些關鍵數據放在程序的共享內存中,這樣就算髮生數據非法訪問等,也能夠在重啓時,直接從共享內存中對數據進行回覆,同時恢復時必須對數據進行校驗,防止將錯誤的數據存入數據庫。
上述大體描述了一個場景型網遊服務器的架構設計,那還有一些細節,好比每一個玩家的完整信息存放在哪裏之類的,玩家對象和場景的關係等等。下面一一分析一下。
4,玩家對象
服務端若是須要對玩家進行邏輯處理,那就必然須要對每一個玩家構建一個對象,以確保處理的對象不會發生錯亂,那這個對象咱們放在哪裏會比較好一些,CLS?MS?ZS。
首先討論討論一下CLS,咱們前面說過,CLS的做用僅僅只是一個鏈接負載的做用,僅僅對客戶端的鏈接進行網絡交互和發給邏輯層處理,不作任何邏輯判斷和內存處理。因此先PASS,那確定是須要放在邏輯層的。
其次若是把玩家對象放在MS上,那全部的玩家都會在MS的內存中保留一份數據信息,好處就是對象統一性,能夠在MS中直接對玩家對象的一些信息進行處理,好比玩家之間溝通,但這無疑加大了MS的內存壓力,同時若是MS接受到一些非法操做信息,有可能就會延遲整個服務器的玩家,甚至掉線。最後舉個最麻煩的例子:假如玩家A在場景1下與怪物PK,因爲對血量數據的記錄是放在MS中,那也就是說ZS必須先對數據進行處理,而後發送給MS進行保存,MS進行統計完後給ZS發送ACK,最後再由ZS反饋給客戶端,這個過程無疑有些麻煩了。
那最後再考慮一下將玩家對象放置在ZS中,這種狀況將對象放在ZS中,好處就是,在ZS中,咱們不管進行什麼操做,ZS均可以直接獲取對象信息,在場景內的交互是很是便利的,不須要多餘的數據傳輸,只是跨場景操做才須要經過MS中轉一下數據。但也有一些缺點,好比全部的對象信息,就須要同時在ZS、MS、數據庫中進行同步,假如咱們須要進行屢次跨服操做,一樣就須要將數據從ZS中提取,傳輸到MS中進行邏輯操做,操做過程當中ZS是不能修改當前對象的相關信息。這樣無疑也是很麻煩的,因此不管放在ZS或這MS上,都是有必定的弊病,須要根據遊戲的具體情景進行分析放置。
5,玩家對象和世界對象的模式關係
⑴,玩家自我操做模式:
好比玩家使用某個做用對象爲本身的物品-加血,流程大體就是服務器中獲取對應的玩家對象,對自身加鎖後,進行處理血量數據的添加,處理完後解鎖,將處理的信息再次反饋給客戶端顯示血量添加。這種自我操做模式不須要考慮其餘對象,僅僅對當前進行操做的對象進行加鎖-處理-解鎖便可,但若是是互動模式,就不能對別的對象進行加鎖操做,不然二者有可能陷入死鎖。那在互動模式下,服務器的操做流程是怎樣呢?看一個圖:
上圖能夠看到,若是對象A對對象B發起進攻,服務器接受到該指令後,須要與B交互,則將該消息打包放入一個消息隊列中,並指定接受者爲對象B,服務器會不斷檢索消息隊列,發現有消息時,就會取出開始執行相應操做,流程與自我操做模式類似,爲B上鎖,減血後,解鎖。通常遊戲的服務器設計中,玩家與玩家之間的交互都是經過消息傳遞交互的。
這種設計模式的優缺點也是比較明顯的:優勢就是在開發人員實現具體的邏輯時,咱們不用擔憂加鎖解鎖的問題,咱們只須要對消息進行邏輯處理而且對客戶端進行反饋就能夠,同時每一個對象都不能直接對其餘對象進行操做,放置死鎖等現象,但缺點就是頻繁的消息傳遞,增長交流的成本,兩個對象之間沒法直接獲取對方的屬性,必須經過消息傳遞,致使邏輯處理會延後,當在線人數太高的狀況下,有可能會形成一些錯誤、超時等等。
⑵,世界型上帝模式:
以一個例子來講明:玩家A向玩家B發起攻擊
流程圖中能夠看出,服務器分別找到A,B兩個對象,將二者鎖住後,直接由服務器進行攻擊的邏輯操做,這裏服務器就是一個能夠訪問全部對象屬性的管理者,這樣作的危險型就是容易發生死鎖,因爲是對多個對象進行加鎖,那多線程下,兩個對象相互攻擊,那就有可能產生死鎖的狀況,因此在進行加鎖前,必須對id先進行排序,保證了加鎖的對象不會產生重複。這種模式的優勢就是邏輯的編寫是比較簡單的,直接對多個不一樣的對象進行邏輯操做就能夠,但有可能致使過多加鎖的衝突和等待,就須要設計人員對總體的框架有着良好的規劃和加鎖策略。
上述就是本文關於場景型網遊服務器設計架構的一個簡單的分析理解,本文只是簡單的走了一遍服務器架構的思想,但具體編碼的不少細節還須要在具體項目中去探索。