天天看點

網遊服務端結構設計 作者:Hack

概述

LoginServer <-----> GameServer

服務端主體分為LoginServer和GameServer, LoginServer做帳戶認證, GameServer做遊戲主邏輯,

中間也可以加一個CharServer啦, 做人物管理, 建立删除人物之類的, 也可以并到GameServer一

起, LoginServer和CharServer都比較簡單, 略過.

通過LoginServer的驗證後将配置設定給Client一個SessionID, 然後與GameServer或CharServer的通信,

都以此SessionID為認證碼. Client隻有發送正确的SessionID才能與GameServer建立連接配接.

2.GameServer層次結構

GameServer分為三層, 網絡層<--->邏輯處理層<--->資料庫層

每層都有一個消息處理隊列, 存放待處理的消息. 消息隊列可以采用先進先出隊列的方式, 也可以

采用堆或者優先隊列的方式, 按優先級對待處理消息進行簡單的排序, 嘿嘿, 是不是有點類似QoS

的思想.

每層采用線程池技術, 預先建立一定數量的空閑線程,  不夠時建立新線程, 過多時則銷毀線程,

保證線程池中有指定數量的空閑線程(Min/Max), 主線程不斷檢查處理隊列是否有待處理消息, 若

有則從線程池中配置設定一空閑線程處理之.

偶在Linux下線程池是用pthread_cond_wait和pthread_cond_signal實作的.

2.1.網絡層

本層根據作業系統不同可以有多種實作, 主要功能是與用戶端建立TCP連接配接, 将TCP流分割成一個個封包,

如果有加密就解密, 如果有壓縮就解壓縮, 加入事務層的處理隊列, 同時把處理隊列中待發送的消息發

送出去, 如果要加密就加密, 如果要壓縮就壓縮.

Windows下采用IOCP模型, Unix-like系統下可采用select/poll(epoll)/kqueue

偶在偶的服務端中采用了select方式, Linux單個端口的連接配接數有限制, 是以偶開了多個線程監聽一組端口,

由LoginServer做負載均衡, 進而保證不會出現某個端口連接配接數過多的情況. 每當有新用戶端要登入時,

LoginServer判斷每個端口的連接配接數量, 選最小發送給用戶端. 偶想這裡也可以做成動态方式,

當每個端口平均連接配接數超過XXXX時, 就開新線程監聽新端口, 并通知LoginServer.

2.2.邏輯處理層

本層是GameServer的核心.

根據操作碼(OPcode)把消息配置設定到每個子子產品裡面處理. 最簡單的方法就是用從0開始的連續的OPcode, 建立

一個與Opcode對應的處理函數的數組, Opcode作為數組的下标, 這樣隻需要O(1)的時間就可以調用到所需的函數

連Hash都省了, 又簡單又高效.

子子產品詳見第7部分

2.3.資料庫層

本層用于資料存儲, 本質上就是把記憶體裡的資料存到硬碟上, 要是你夠拽的話, 可以不用現有的資料庫,

自己寫算法存儲文本檔案, 但為了友善起見, 也為了提高效率, 還是用資料庫比較好.

windows下用MSSQL, 或者用MYSQL,

Unix-like系統下可以用的就多了, 能多相容幾種資料庫最好

MYSQL的性能優異, 功能上稍差一點, 如果不需要用到存儲過程的話, MYSQL還是首選的.

資料庫層一般用單線程已經足夠, 可以不需要做對象互斥, 程式設計方面也會簡單一點. 但是需要注意的是,

資料庫操作方面一定要用Transaction, 可以有效防止複制現象發生, 比如:交易操作一旦發生錯誤, 則

rollback到交易之前, 不會發生錢已交出, 東西卻沒拿到的情況.

3.消息格式定義

3.1.網絡層<-->邏輯層消息格式(網絡封包格式)

3.2.邏輯層<-->資料庫層消息格式

4.遊戲對象定義(Object)

object

  |-------> item

  |           |----> container(容器類對象,如倉庫、背包等)

  |

  |-------> unit

  |           |-----> player

  |           |-----> monster

  |           |-----> npc

  |           |-----> corpse(屍體對象)

  |

  |-------> gameobj

              |-----> dynamicobj(如技能産生的臨時對象)

5.地圖場景管理

6.腳本系統

7.邏輯層子產品化設計  

對于地圖場景管理打算采用這種方式

在伺服器上把場景劃分為小區域(視野大小)。每個區域對應一個list,場景中的所有對象按他們的位置加入到對應區域的list中,那麼每次行走隻需要把消息發送給幾個相臨區域的Player

一個建議:

可以把GameServer的網絡層剝離出來做成一個應用級網關(Application-level Gateway),因為這部分雖然邏輯簡單但消耗資源确是比較大的。做成單獨的可以使你的系統伸縮性更好,GameServer和Gate可以是1:N的 

呵,小高現在也喜歡用些時髦名詞了。

說到伺服器架構,樓主的設計簡單了些。對于一個分布式伺服器架構來說要考慮的問題就很多,這裡随便談一些體會吧:

首先要有一套設計良好的應用伺服器架構和通訊中間件,應用伺服器是上層應用的重要基礎設施,用來實作諸如服務定位、名字注冊、負載平衡、服務叢集、故障重起、時間服務、LOG服務等等功能,通訊中間件也是不能少的,如果伺服器之間通訊用手工寫socket通訊的話,那很快你就會被協定處理、各種微妙的時序、同步等問題所困擾。可以說,你的伺服器架構能實作到多複雜能堆多高,關鍵就看這兩樣實作的有多堅固耐用。

其次對象狀态的序列化很重要,這關系到對象同步、對象persistent、狀态遷移、故障恢複等等諸多問題。問題遠不是把對象狀态寫到一個流裡就完事那麼簡單的,做好這一塊那就等于解決了一半的伺服器邏輯問題了。

然後再談談分布的問題,基本上有兩種思路,一種是按邏輯功能劃分計算資源,一種是按容量劃分計算資源。按邏輯功能劃分是比較容易想到一種方式,簡單的說就是将不同的功能子產品實作到不同的程序裡,這種方案看似不錯,實則問題多多。首先是各子產品之間必然要進行互動,這樣就會引起很多細粒度的遠過程通訊,一來造成性能損失二來增加了實作難度,其次是各子產品負載并不均衡,很容易産生性能瓶頸,也很難實作負載平衡,另外這樣的伺服器程式調式起來将是一場噩夢。當然,凡事都沒有絕對,一切設計都要根據實際情況來決定,有時候該按功能劃分的還是要按功能劃分,比如小高建議的劃分一個應用級網關出來在某些情況下就很有效。現在已有的一些我們所能見到的超大無縫連接配接世界的分布式實作,其基本的思想是将世界劃分成一系列相連的塊,為每個塊配置設定計算資源來實作并行分布運算。關鍵的問題在于處理相臨塊邊界之間的同步問題,如果處理不好就可能造成同步失調、物品複制等等BUG,這需要一個設計良好的同步算法。關于計算資源的配置設定我們也 可以有兩種選擇,一種是使用性能強勁的多CPU伺服器,在單程序裡用多線程并行計算,即所謂的SMP架構,這樣的優點是簡單、高效,缺點是成本高,伸縮性不高。另一個就是伺服器叢集架構了,這樣實作起來更複雜、而且伺服器的瓶頸大多出在I/O而非計算上,使用叢集以後增大了伺服器之間的通訊量,設計不好反而造成性能損失,不過優點也很明顯,降低成本,提高伸縮性,通過良好的設計還能實作故障恢複(也就是叢集中任何一台伺服器當機都不會影響遊戲的運作)。

最後再談談遊戲邏輯的處理,一種設計是讓伺服器處于被動狀态,隻有客戶請求到達再進行邏輯處理,重新整理對象狀态,這樣可以降低一些計算量,但是因為伺服器不進行對象狀态的模拟,缺乏一些必要的資訊,會限制一些功能(特别是真三維世界運動)的實作。我現在更傾向于傳統遊戲的做法,讓伺服器運作一個遊戲邏輯循環,定時的模拟對象狀态。