1. 程式人生 > >微信房卡牛牛源碼架設服務器架構: 總體設計

微信房卡牛牛源碼架設服務器架構: 總體設計

流程 asio 查詢 postgres 邏輯模塊 加鎖 測試報告 詳細 cps

微信房卡牛牛源碼架設(aqiulian.com/h5)QQ:212303635 首先要說明的是, 這個棋牌遊戲的服務器架構參考了網狐棋牌的架構。網狐棋牌最令人印象深刻的是其穩定性和高網絡負載。它的一份壓力測試報告上指出:一臺雙核r的INTEL Xeon 2.8CPU加上2G內存和使用共享100M光纖的機子能夠支持5000人同時在線遊戲。

在研究其服務器框架後發現,它的網絡部分確實是比較優化的。它主要采用了Windows提供的IO完成端口來實現其網絡組件。本服務器雖然參考了其設計,但是還是有很大的不同,因為這個服務器框架主要是用在linux系統之上,而網狐棋牌是基於Windows平臺的,嚴重依賴於windows sdk。這個架構延續了網狐棋牌在網絡組件所作的努力,這個棋牌的服務器也使用異步IO作為網絡的工作方式,更為徹底的是其數據庫也是采用異步架構。boost::asio提供了一個異步框架,所以它的幾個核心組件: TCPServerService, TimerService, DatabaseService, AsyncService中都可以看到boost::asio的影子。

, 圖1是總體架構圖。從圖上我們看到服務器的整體架構分為三層:Libraries, Core和Applications。Core層基於Libraries實現,而Applications使用Core層提供的服務,並且要監聽Core層的異步事件(Socket、Database等)。

技術分享圖片

   圖1 棋牌遊戲服務器端總架構

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

技術分享圖片

圖2 CenterServer與外界的交互圖

CenterServer不直接與玩家進行交互,它主要的功能是管理遊戲列表和房間信息,包括:

1. 遊戲類型信息: 棋牌遊戲、休閑遊戲、視頻遊戲等。

2. 遊戲種類: 比如在棋牌遊戲這個大類之下有:德州撲克、鬥地主、升級等。

3. 站點信息: 因為這個服務器架構完全支持分布式,所以還保存有站點的信息

4. 房間信息: 維護當前有哪些房間以及房間當前的在線人數。

CenterServer中有關遊戲列表的信息是它在啟動的時候從ServerInfoDB這個數據庫加載的, 而它的房間信息來自RoomServer,RoomServer在啟動時將自己註冊進來,在關閉的時候從CenterServer中註銷自己。同時在玩家進入房間的時候,還會要求CenterServer更新在線人數。

  CenterServer還應該響應LogonServer和RoomServer的請求,將遊戲列表和房間信息返回給它們。

3.2 LogonServer

技術分享圖片

圖3 LogonServer與外界交互圖

LogonServer提供註冊新的遊戲玩家服務並且處理遊戲玩家的登錄請求。

LogonServer需要和UserInfoDB交互,這些交互包括:

1. 在註冊的時候寫入註冊玩家的信息。

2.在玩家登錄的時候與數據庫玩家信息進行核對。

LogonServer會定時地向CenterServer發送更新遊戲列表和房間信息的請求,因為這些信息在不斷地變化,而LogonServer需要在玩家登錄時將這些信息返回給他們。

3.3 LogServer

技術分享圖片

圖4 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,這個可以根據玩家量按需架設。圖5給出了RoomServer與外界交互的圖。

技術分享圖片

圖5 RoomServer與外界的交互圖

RoomServer啟動的時候,先要發送請求給CenterServer進行註冊,在關閉時要從CenterServer中註銷。同時還會定時通知CenterServer更新在線人數, 定時從CenterServer上取回最新的遊戲列表和房間信息。

RoomServer需要和玩家進行交互。玩家進入房間,找桌子座下等的請求都由RoomServer來處理,而遊戲操作。比如說加註、發牌等 RoomServer會直接轉發給遊戲模塊進行處理。

RoomServer管理著一個在線用戶列表,在玩家進入房間,離開房間時這個列表隨之更新。這個列表中有關玩家的詳細信息是從數據庫UserInfoDB中加載到的。 玩家在進行遊戲時,由於輸贏的關系,他的積分或者遊戲幣會隨著變化,為了記錄這些變化, 需要與GameDB進行交互。

管理員可以通過RoomServer來發布消息、踢出玩家、警告玩家、設置玩家權限、設置房間屬性等活動。

玩家也可以通過RoomServer參與聊天(包括大廳公聊和私聊)。

4 交互協議

客戶端和服務器進行交互時,傳遞的包需要使用protobuf來序列化。一個請求由一個container組成,container中可以包含一個或者多個請求包/應答包。每一個請求包和應答包都有如下基本結構:

技術分享圖片

圖6 服務器和客戶端通信的Package結構

nMainCmd 指示請求的類別,比如說遊戲請求,房間管理請求等

nSubCmd 指請求的具體是什麽,比如加註、踢出玩家等

nDataSize 指示pData字段的長度

pData 可以是任何消息,如果是一個結構,需要用protobuf序列化

5 數據庫

Database主要有3個: ServerInfoDB、UserInfoDB, GameDB。

ServerInfoDB: 主要存儲的是遊戲列表的信息。這些信息包括—遊戲種類列表、遊戲類型列表和站點信息。

UserInfoDB: 主要存儲玩家相關的全局信息,包括玩家的 ID 號碼,帳戶名字,密碼,二級密碼,頭像,經驗數值,登陸次數,註冊地址,最後登陸地址等玩家屬性信息。

GameDB: 主要存儲的是玩家的遊戲相關信息,例如遊戲積分,勝局,和局,逃局,登陸時間等信息

微信房卡牛牛源碼架設服務器架構: 總體設計