1. 程式人生 > >h5牛牛平臺搭建中小型棋牌類網絡遊戲服務端架構

h5牛牛平臺搭建中小型棋牌類網絡遊戲服務端架構

tin timer redis緩存 公眾平臺 客戶端 三種 參考 組件 初始

h5牛牛平臺搭建相關教程:http://h5.mostsheng.com

相關聯系方式:QQ1279829431電聯17061863554

Golang實現基礎架構邏輯後,準備再次談談我的想法。

已實現的邏輯與前文描述有幾點不同:

1. Gateway更名為Proxy,DBProxy更名為DB

2. Proxy同時持有與(Login, Game)不同類型服務器的多條連接

3. DB不參與負載均衡,考慮是棋牌數據庫負載不高,即使需要擴展多個也可以通過不同服務器配置指向不同的DB來擴展

4. 消息頭格式以源碼實現的為主

5. 心跳機制在不考慮客戶端的前提下,服務端會主動發送心跳包,但並非總是特定間隔時間發送

如果閱讀起來感覺晦澀難懂,那就不妨直接看源碼

Network

包含Server,Client,RPC三個組件。

Server

網絡服務端組件,持有監聽套接字,管理所有客戶端連接(map存儲,value可以存儲自定義值),一個客戶端連接到來起一個Goroutines接收消息,調用Stop將等待所有Goroutines退出才返回,只通知業務邏輯“收到消息”“連接關閉”兩種事件。

Client

網絡客戶端組件,持有與服務端的連接,實現補償策略重連機制,只通知業務邏輯“客戶端連接成功”“客戶端收到消息”兩種事件。

RPC

遠程過程調用,只實現同步調用,同一時間一個調用占用一條連接,實現連接池,暫時沒有限制最大連接數。

註:服務端重啟,一次調用將會關閉所有已經建立的連接,創建新的連接完成調用,若服務端重啟歷時過長,此時調用將因為連接服務端失敗而返回錯誤。

Error

有必要詳說通用的錯誤機制,錯誤類型是跨服務器跨網絡的,可以直接返回error或MyError類型通知通訊調用方錯誤,錯誤會一級一級返回,Golang內置error類型終會創建相應的MyError自定義錯誤類型進行返回,RPC接收方收到消息將會檢查若為錯誤消息,則創建MyError返回標識調用失敗。

Server

Proxy(代理), Manager(管理), Login(登陸), Game(遊戲), DB(數據庫代理)。

Proxy

持有

Server組件:接受客戶端連接,管理客戶端連接,事件到來通知業務邏輯。

持有Client組件:連接Manager服務器,事件到來通知業務邏輯。

連接Manager成功,將會發送註冊服務消息(編號,監聽地址,服務類型)。

客戶端消息到來,檢查conn上是否綁定的有session,沒有則創建綁定,最後消息總是交由session處理。

客戶端連接關閉,檢查conn上是否綁定的有session,若有則關閉session。

收到Manager通知服務消息,合理更新本地可用服務信息。

Session

會話目前共持有client(客戶端), login(登陸), game(遊戲)三個連接,存在最基本的目的就是消息轉發。

session共有三種連接狀態,安全狀態(說明客戶端一直給服務端發消息,平均間隔時間不超過十秒),警告狀態(客戶端至少最近十秒內沒有給服務端發消息),死亡狀態(客戶端至少最近二十秒內沒有給服務端發消息,且連服務端十秒前發送的心跳包都不回復),session收到客戶端消息,總是將session狀態設置為安全狀態。

目前也只有這層實現心跳檢測保活(服務端和服務端之間是沒有心跳的),一個session起一個Goroutines間歇循環保活(原本也想所有session統一保活,最大的好處就是只用起一個Goroutines,但是這樣必然要維護session集合,因為牽涉增加刪除session,必然又要加鎖,不想這樣所以采用了當前的方案),每十秒檢查一下session狀態,總是將狀態值遞減至死亡狀態,當session狀態是警告狀態,嘗試向客戶端發送心跳包,當session狀態是死亡狀態,則關閉session。

總是在收到客戶端快速註冊消息時,關閉原有與登陸建立的連接,重新創建連接,起一個Goroutines接收登陸服務端消息,不在客戶端連接建立的時候連接登陸服務端,這樣做會多一層保護,防止攻擊者惡意建立連接,穿透至登陸服務端,這裏會重新序列化客戶端消息,填充客戶端地址,畢竟只有這裏知道客戶端的真實地址,發向登陸服務端的其它類型消息不允許先於快速註冊消息,否則直接關閉session,接收登陸服務端快速註冊消息返回時會解析消息,取出用戶編號記錄到session裏,便於快速登陸遊戲服務端時填充用戶編號。

總是在收到客戶端快速登陸消息時,關閉原有與遊戲建立的連接,重新創建連接,起一個Goroutines接收遊戲服務端的消息,客戶端快速登陸消息只需要提供“遊戲類型(鬥地主)”“遊戲等級(新手房)”,這裏通過查找對應服務建立正確連接,這裏會重新序列化消息,新增“用戶編號”“時間戳”“簽名”,時間戳和簽名只是想保證遊戲服務端收到的快速登陸消息一定是我的代理服務端發出的(詳細請參看源碼,思路可參考微信公眾平臺接入驗證),發向遊戲服務端的其它類型消息不允許先於快速登陸消息,否則直接關閉session。

用戶斷開與代理的連接,將會觸發關閉session,通知保活Goroutines退出,關閉與登陸遊戲之間的連接,兩個接收消息Goroutines將退出,與客戶端的連接接下來會被釋放,不再有誰綁定該session,所以該session將會被GC(垃圾回收)。

不管接收登陸還是遊戲消息失敗,接收Goroutines都會退出,將會關閉與客戶端連接,進而關閉session。

收到用戶登出消息,session直接關閉與遊戲服務端的連接。

Manager

持有Server組件:接受客戶端(Proxy, Login, Game)連接,管理客戶端連接,事件到來通知業務邏輯。

維護兩個服務集合,一是所有服務(所有已開啟的服務),二是已選服務(負載均衡策略後選擇的服務)。

收到註冊服務消息,記錄對應網絡連接,所有服務表記錄該服務,若已選服務表中不存在類似服務,則同時已選服務表記錄該服務,若註冊服務類型是代理,則通知當前已選服務,用來初始化代理本地可用服務。

連接關閉,將通過連接查找服務,所有服務表刪除該服務,已選服務表若存在該服務則刪除,嘗試從所有服務表中獲取相似服務,若獲取到則添加至已選服務表。

收到更新計數消息,更新對應服務的計數值,只有當計數值高於容量閾值時才會觸發後面的邏輯,已選服務表若存在該服務則刪除,嘗試從所有服務表中獲取相似服務,若獲取到則添加至已選服務表,但若準備刪除和增加的是同一個服務,則不觸發前面的刪除添加服務邏輯。

收到開啟服務消息,設置對應服務開啟標識,若已選服務表中不存在類似服務,則已選服務表記錄該服務。

收到關閉服務消息,設置對應服務關閉標識,已選服務表若存在該服務則刪除,嘗試從所有服務表中獲取相似服務,若獲取到則添加至已選服務表。

前面有三處用到獲取相似服務,其實這裏實現了負載均衡,每次調用總會返回計數值最小的Proxy, Login服務,計數值最大的卻又不超過容量閾值的Game服務(若都超過閾值返回最小的),讓用戶盡可能在相同遊戲服務中玩,便於快速組桌開始遊戲。

前面提到的不管是增加已選服務,還是刪除已選服務,都會通知所有已註冊的代理。

Login

持有Server組件:接受Proxy模擬客戶端建立的連接,管理與Proxy建立的連接,事件到來通知業務邏輯。

持有Client組件:連接Manager服務器,事件到來通知業務邏輯。

持有RPC組件:連接DB數據庫代理。

連接Manager成功,將會發送註冊服務消息(編號,監聽地址,服務類型)。

收到快速註冊消息,RPC同步調用請求數據庫代理,不管是查詢已有用戶還是創建新用戶,數據庫代理返回後,直接回復客戶端,其實是回復給代理。

Game

持有Server組件:接受Proxy模擬客戶端建立的連接,管理與Proxy建立的連接,事件到來通知業務邏輯。

持有Client組件:連接Manager服務器,事件到來通知業務邏輯。

持有RPC組件:連接DB數據庫代理。

連接Manager成功,將會發送註冊服務消息(編號,監聽地址,遊戲類型,遊戲等級,服務類型)。

收到快速登陸消息,通過校驗簽名確保是由Proxy發來的,RPC同步調用請求數據庫代理查詢用戶信息,數據庫代理返回後,直接回復客戶端,其實是回復給代理。

DB

持有Server組件:接受客戶端(Login, Game)連接,管理客戶端連接,事件到來通知業務邏輯。

這裏擴展了OnMessage的返回值,支持返回nil表示成功,返回內置error類型表示錯誤,返回自定義MyError類型表示成功或錯誤,返回其它數據結構表示回復給客戶端的內容。

連接MySQL數據庫,連接Redis緩存

註:已經實現定時器模塊,定時時間精確到秒,主要邏輯只不過是對標準庫Timer、Ticker封裝管理而已,通過全局唯一編號添加定時器,支持循環定時器,支持獲取到期剩余時間。

h5牛牛平臺搭建中小型棋牌類網絡遊戲服務端架構