棋牌遊戲服務器架構: 總體設計
首先要說明的是, 這個棋牌遊戲的服務器架構參考了網狐棋牌的架構。網狐棋牌最令人印象深刻的是其穩定性和高網絡負載。它的一份壓力測試報告上指出:一台雙核r的INTEL Xeon 2.8CPU加上2G內存和使用共享100M光纖的機子能夠支持5000人同時在線遊戲。在研究其服務器框架後發現,它的網絡部分確實是比較優化的。它主要采用了Windows提供的IO完成端口來實現其網絡組件。本服務器雖然參考了其設計,但是還是有很大的不同,因為這個服務器框架主要是用在Linux係統之上,而網狐棋牌是基於Windows平台的,嚴重依賴於windows sdk。這個架構延續了網狐棋牌在網絡組件所作的努力,這個棋牌的服務器也使用異步IO作為網絡的工作方式,更為徹底的是其數據庫也是采用異步架構。boost::asio提供了一個異步框架,所以它的幾個核心組件: TCPServerService, TimerService, DatabaseService, AsyncService中都可以看到boost::asio的影子。
, 是總體架構圖。從圖上我們看到服務器的整體架構分為三層:Libraries, Core和Applications。Core層基於Libraries實現,而Applications使用Core層提供的服務,並且要監聽Core層的異步事件(Socket、Database等)。
棋牌遊戲服務器端總架構
Libraries 主要由4個庫組成,其中boost::thread是一個跨平台的線程庫,boost::asio是跨平台的異步IO庫,protobuf則是用來序列化服務器和客戶端協議的, libpq是開源數據庫postgresql提供的客戶端的官方接口,支持異步數據庫操作。
Core 主要由4個Service組成,它們建立在Libraries的基礎之上。給應用層提供了網絡,數據庫和定時器功能。AsyncService主要是Core內部自己使用。TimerService提供定時器功能,TCPServerServic管理著客戶端來的連接。而DatabaseService提供基本的數據庫訪問功能。
Applications是基於Core實現的4種服務器,它們管理著遊戲信息,提供登錄以及處理遊戲邏輯的功能。下麵是用戶與這些服務器交互的一個經典流程:
1) 客戶端將用戶名和密碼發送給LogonServer登錄,在登錄驗證成功以後,將遊戲列表返回給客戶端。
2) 玩家選擇具體遊戲進入房間時,客戶端發送請求給RoomServer,RoomServer將房間的信息返回給客戶端顯示
3) 玩家選擇桌子坐下,遊戲開始。客戶端將遊戲動作發送給相應的RoomServer, RoomServer將操作解析後轉發給遊戲邏輯模塊進行處理,並將處理結果返回給客戶端。
這幾個服務器這間的關係是:
1) CenterServer維護遊戲列表信息和房間信息;
2) LogonServer定時從CenterServer取回遊戲列表信息和房間信息;
3) RoomServer在啟動時向CenterServer注冊,在關閉時從CenterServer注銷, 以玩家進入房間時通知CenterServer更新在線人數。同時像LogonServer一樣定時連接CenterServer更新遊戲列表和房間信息。
1 Libraries層
boost::asio是一個異步IO庫,提供了一個通用的異步框架,並提供了基本的socket的異步接口,它的主要功能是響應程序的異步IO請求,在操作完成以後,將其加入到一個完成隊列之中, 在這個完成隊列上有一些工作線程在等著,這些工作線程從完成隊列上取出已經完成的操作,調用上層應用提供的一個完成函數--completaion handler。asio庫是通過學實現Proactor模式來完成這些工作的,在Windows是直接基於I/O completion port,而在類Unix係統中,是基於epool等函數使用Reactor模式來模擬的。
libpq是開源數據庫postgresql提供的客戶端接口庫。這裏選用postgresql是因為postgresql的跨平台性以及其穩定性和高性能,另一方麵是由於我對這個數據庫比較地熟悉。Libpq也對數據庫的連接、查詢、更新等提供了異步實現。可以和boost::asio結合在一起提供統一地異步操作接口。
boost::thread庫是用C++實現的一個跨平台的線程庫, 在C++11中,它已經被納入到了標準庫中。這個庫在這裏主要用來實現一個線程池,作為boost::asio的工作線程。主要是由Core層的AsyncService來維護。代碼的其他地方不直接啟動線程。但是在異步操作的完成函數中,對那些共享數據需要加鎖保護。
protobuf庫是Google發布的一個開源的用來序列化對象的高性能的庫,它支持多種語言,比如C++,Java,flash 等等。同時還將字節序等瑣碎的東西封裝起來了,方便上層應用。
2 Core層
核心層由4個Service: AsyncService、TCPServerService、TimerService、DatabaseService組成。下麵是關於它們的基本描述.
AttemptService是Core內部使用的,它封裝了boost::asio和ThreadPool的功能,提供給其他幾個Service使用。從名字上可以看出,他的主要功能是給其他幾個Service提供異步調度,這是通過boost::asio提供的功能來實現的,而ThreadPool是提供給boost::asio作為工作線程的。
TCPServerService有一個連接池,管理著客戶端來的連接。內部通過AsyncService將socket讀寫完成消息,通過應用層注冊進來的TCPServiceObserver通知到調到應用層去。它和Applications的交互包括:
1) Applications 調用 SetObserver注冊用來接收網絡讀寫完成消息;
2) Applications 調用 SendData 發送數據;
3) Core在accept, recv完成後調用 Applications注冊的Observer。
TimerService提供了定時器的功能,Applications層可以直接使用它來創建定時器,取消定時器。設定時間到來時,TimerService會調用創建定時器時指定的一個回調函數。
DatabaseService封裝了libpq,提供數據庫的基本操作。主要管理數據庫連接,執行查詢操作,執行存儲過程等。它的實現中有一個連接池。和socket操作一樣,它提供的數據庫操作都是異步執行的,所以Applications層需要實現DBServiceObserver來監聽操作結果。
3 Applications
前麵的無論是libraries還是core,都是死的,隻有applications加入了邏輯,它們是棋牌服務器的主休。下麵是關於它們的比較詳細的信息
3.1 CenterServer
CenterServer與外界的交互圖
CenterServer不直接與玩家進行交互,它主要的功能是管理遊戲列表和房間信息,包括:
1. 遊戲類型信息: 棋牌遊戲、休閑遊戲、視頻遊戲等。
2. 遊戲種類: 比如在棋牌遊戲這個大類之下有:德州撲克、鬥地主、升級等。
3. 站點信息: 因為這個服務器架構完全支持分布式,所以還保存有站點的信息
4. 房間信息: 維護當前有哪些房間以及房間當前的在線人數。
CenterServer中有關遊戲列表的信息是它在啟動的時候從ServerInfoDB這個數據庫加載的, 而它的房間信息來自RoomServer,RoomServer在啟動時將自己注冊進來,在關閉的時候從CenterServer中注銷自己。同時在玩家進入房間的時候,還會要求CenterServer更新在線人數。
CenterServer還應該響應LogonServer和RoomServer的請求,將遊戲列表和房間信息返回給它們。
3.2 LogonServer
LogonServer與外界交互圖
LogonServer提供注冊新的遊戲玩家服務並且處理遊戲玩家的登錄請求。
LogonServer需要和UserInfoDB交互,這些交互包括:
1. 在注冊的時候寫入注冊玩家的信息。
2.在玩家登錄的時候與數據庫玩家信息進行核對。
LogonServer會定時地向CenterServer發送更新遊戲列表和房間信息的請求,因為這些信息在不斷地變化,而LogonServer需要在玩家登錄時將這些信息返回給他們。
3.3 LogServer
LogServer與外界的交互圖
有時候,玩家可能會對遊戲的過程產生懷疑,或者想回顧整個遊戲的過程。這就需要服務器將遊戲的過程以Log的形式存儲起來,供玩家檢查用。LogServer的就是用來響應玩家的核查的請求,然後從GameLogDB中將整個遊戲過程返回給客戶端,客戶端以視頻地方式顯示給玩家。
玩家在請求檢查的時候,客戶端會將這局遊戲的以及玩家的信息id發送到LogServer, LogServer根據遊戲id的信息從GameLogDB取出日誌信息返回給玩家。遊戲的過程可以用結構化語言描述出來,本來postgresql直接支持Json,也就是說Log可以以JSON的形式存在數據庫之中,但是由於可能會有字節序的問題,所以Log的信息也要用protobuf序列化了再存入數據庫。LogServer在從數據庫中讀出日誌後不用反序列化直接返回給客戶端反序列化。
3.4 RoomServer
RoomServer可能是最重要的一類Server了,一個RoomServer會和一個遊戲模塊結合在一起。它管理著遊戲的一個房間,處理玩家進入房間,找桌子座下的請求,並將遊戲相關的消息轉發給遊戲模塊進行處理。不僅不同的遊戲會有不同的RoomServer,即便是同一遊戲,也可能有多個RoomServer, 比如對於德州撲克來說,就可能有vip房間,普通房間等等,同一類型的房間也可能有Room1,Room2,這個可以根據玩家量按需架設。給出了RoomServer與外界交互的圖。
RoomServer與外界的交互圖
RoomServer啟動的時候,先要發送請求給CenterServer進行注冊,在關閉時要從CenterServer中注銷。同時還會定時通知CenterServer更新在線人數, 定時從CenterServer上取回最新的遊戲列表和房間信息。
RoomServer需要和玩家進行交互。玩家進入房間,找桌子座下等的請求都由RoomServer來處理,而遊戲操作。比如說加注、發牌等 RoomServer會直接轉發給遊戲模塊進行處理。
RoomServer管理著一個在線用戶列表,在玩家進入房間,離開房間時這個列表隨之更新。這個列表中有關玩家的詳細信息是從數據庫UserInfoDB中加載到的。 玩家在進行遊戲時,由於輸贏的關係,他的積分或者遊戲幣會隨著變化,為了記錄這些變化, 需要與GameDB進行交互。
管理員可以通過RoomServer來發布消息、踢出玩家、警告玩家、設置玩家權限、設置房間屬性等活動。
玩家也可以通過RoomServer參與聊天(包括大廳公聊和私聊)。
4 交互協議
客戶端和服務器進行交互時,傳遞的包需要使用protobuf來序列化。一個請求由一個Container組成,container中可以包含一個或者多個請求包/應答包。每一個請求包和應答包都有如下基本結構:
服務器和客戶端通信的Package結構
nMainCmd 指示請求的類別,比如說遊戲請求,房間管理請求等
nSubCmd 指請求的具體是什麼,比如加注、踢出玩家等
nDataSize 指示pData字段的長度
pData 可以是任何消息,如果是一個結構,需要用protobuf序列化
5 數據庫
Database主要有3個: ServerInfoDB、UserInfoDB, GameDB。
ServerInfoDB: 主要存儲的是遊戲列表的信息。這些信息包括—遊戲種類列表、遊戲類型列表和站點信息。
UserInfoDB: 主要存儲玩家相關的全局信息,包括玩家的 ID 號碼,帳戶名字,密碼,二級密碼,頭像,經驗數值,登陸次數,注冊地址,最後登陸地址等玩家屬性信息。
GameDB: 主要存儲的是玩家的遊戲相關信息,例如遊戲積分,勝局,和局,逃局,登陸時間等信息
(有什麼問題歡迎指教,企鵝:2172243813 更多資訊盡在www.ttkmwl.com)
最後更新:2017-08-31 17:02:47