天天看點

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

      核心的幾個元件被設計成Service,也就是說這幾個子產品都要實作如下接口:

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖1  IService接口

      Start方法用來啟動服務。

      Stop 方法用來關閉服務。

      IsService 方法用于查詢目前服務是否正在工作。

      核心中的幾個Service都不能夠直接建立,Applications在使用這些Service的時候首先要得到一個IServiceMgr的執行個體,這被實作成了一個另類地單例模式。IServiceMgr的接口定義如下:

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖2  IServiceMgr接口

      IServiceMgr提供兩類接口:

      1) 擷取Service的接口,這樣直接得到具體的Service,是因為核心的Service比較固定。沒有必要用GetService(strServiceName)這種方法。

          GetAsyncService    傳回AsyncService的執行個體

          GetDBService        傳回DatabaseService的執行個體

          GetTCPService       傳回TCPServerService的執行個體

          GetTimerService    傳回定時器執行個體

       2) 一個靜态的單例方法Instance。它申明在接口層,但是需要在IServiceMgr的實作中去實作它。它傳回IServiceMgr的執行個體。

      由于IServiceMgr的實作隻是簡單地将IAttemptService,ITCPServerServer, IDatabaseService,ITimerService的實作組合在了一起,是以它的實作不會較長的描述。

1 AsyncService詳細設計

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖3  AsyncService的詳細設計

      AsyncService主要是提供給其他3個Service使用的,它實作了IService接口和IAsyncService接口。因為與異步相關的功能基本上都被boost::asio實作,是以AsyncService主要隻是管理boost::asio的執行個體 。IAsyncService隻提供了一個方法:

      GetIOService      傳回一個可用的boost::asio::io_service的執行個體

      AsyncService組合了boost::asio和ThreadPool,其中boost::asio::io_service的數目和機器的cpu總數相同,而ThreadPool中線程總數為2倍的cpu數。所有ThreadPool中的線程都将作為工作線程,它們的入口函數都是io_service::run。

2 TimerService詳細設計

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖4  TimerService的結構圖

      TimerService實作了IService和ITimerService接口。ITimerService提供如下接口:

       1) SetTimer(timerId,milisecs,timerFunc,repeatTimes)    設定一個id為timerId的定時器,這個定時器會被激            

           發repeatTimes次,每兩次被小激發的時間間隔為millsecs毫秒。每次被激發都會調用 timerFunc這個函數。

       2) KillTimer(timerId)   取消id為timerId的定時器。

       3) KillAllTimer()           取消所有的定時器,一般用在系統關閉時調用。

       TimerService管理着一些TimerItem,Applications層用一個新的timerId,調用SetTimer時,TimerService就會建立一個新的TimerItem, 而在調用KillTimer時,就會銷毀掉與其相關的那個TimerItem。TimerService的實作依賴與AsyncService,因為定時器本質上也是異步操作。将由AsyncService中的io_service來統一排程。      

       需要注意以下幾點:

        1) 傳給SetTimer的timerFunc這個函數要是線程安全的,因為不确定會在哪個工作線程的context中調用它,同時             如果你的好幾個定時器公用同一個timerFunc, 就可能對共享資源造成競争。

        2) SetTimer進如果發現已經存在相同id的TimerItem, 不會建立一個新的TimerItem,而是取消先前的定時器。修            改其參數後啟動。

3 TCPServerService詳細設計

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖5  TCPServerService結構圖

      TCPServerService實作了IService接口和ITCPServerService接口。ITCPServerService的幾個主要接口說明:

      1) SendData  通過指定的ISocketItem發送資料, 資料在一般情況下由4個參數: MainCmd, SubCmd, Data, DataSize (可以參與總休設計中關于協定的部分的描述) 。有的時候Data為空,就不需要Data和DataSize這兩個參數了。

      2) SendDataBatch 給所有連接配接發送資料。這是批量發送的,所有連接配接池中對應的用戶端都會收到。

      3) CloseSocket  關閉指定的連接配接。

      4) SetObserver 設定監聽者。用以接收異步通知。

      TCPServerService 管理着一個用戶端來的連接配接池。這個連接配接池由SocketItem組成,每一個SocketItem都與一個整數辨別對應,Applications使用這個辨別來發送資料和接收資料。SocketItem主要提供下面幾個接口:

      1) GetIndex    擷取與其對應的唯一辨別

      2) GetRound   由于每個SocketItem都是可以重用的,是以為了防止混亂,比如說一個SocketItem在前一時刻對應着client1, 但是現在對應着client2。client1曾經的一個請求現在才要傳回,這時如果沒有GetRound就會把client1的處理結果錯誤地傳回給client2。從這裡也可以看出,每個SocketItem的round是在連接配接建立的時候會增加。

      3) IsConnected  是否處于連接配接狀态。

      4) SendData  發送資料。

      5) GetClientAddress 得到用戶端的IP位址

      6) GetConnectTimer 擷取連接配接時間。

      7) Close  關閉連接配接。

      也許你會問了,我怎麼隻看到發送資料的接口,而沒有接收資料的接口呢?因為這是個異步架構,在有連接配接到來,或者資料到來的時候,你會收到通知的。前提條件是你調用SetObserver設定了監聽者。TCPServerService的監聽都需要實作ITCPServiceObserver接口, TCPServerService通過這個接口提供的方法來通知你連接配接和讀取事件:

      1) OnSocketAccept  在新連接配接到達時,會調用你這裡面的内容。

      2) OnSocketRead  在資料讀取完成後,會調用你提供的這個方法做進一步處理。

      3) OnSocketClose  告訴你連接配接将要關閉。

      需要注意的是如果你這三個方法中有共享的資料,要加鎖保護。因為工作線程可能會産生競争狀态。

      和TimerService一樣,TCPServerService的異步排程依賴于IAsyncService。

 4 DatabaseService詳細設計

棋牌遊戲伺服器架構: 詳細設計(一) 核心設計

圖6  DatabaseService結構圖

      可以對比一下DatabaseService和TCPServerService的結構圖,你會發現他們是那麼地相似。對的,它們的設計思路如出一轍。DatabaseService實作了IService和IDatabaseService這兩個接口。IDatabaseService主要隻提供了3個接口:

      1) Connect  連接配接到一個資料庫

      2) Query   進行查詢。 這裡有兩點要注意:1) Query以後不會立馬得到結果,因為這是異步的; 2) 存儲過程的調用也得使用這個方法,你隻要将query語句寫成 \'select stroage_procedure(param1,param2,...)\' 就行了。

      3) SetObserver 設定觀察者。因為查詢是異步的,是以你要設一個觀察者來得到通知。

      DatabaseService管理着一些資料庫連接配接DBConnect, 每一個DBConnect也與一個整數辨別相關聯,可以通過GetIndex獲得。同時你可以通過IsConnect來查詢這個DBConnect是否處于連接配接狀态。

       在實作IDBServiceObserver時,你需要實作下面兩個方法:

       1) OnDBConnect  在資料庫連接配接建立時會調用

       2) OnQueryEnd  在這裡你可以得到一個表示查詢結果的QueryResult對象。你可以通過它知道查詢的狀态,以及結果資訊。