go 語言遊戲服務端開發——架構

**網絡遊戲程序分爲客戶端和服務端:**客戶端負責圖形渲染、交互和一些簡單校驗處理,服務端負責業務邏輯處理、數據存儲。

我們開發一個遊戲 demo,服務端程序可以是一個單線程的服務進程。它包含網絡通信、業務邏輯處理、數據存儲。服務端打開網絡端口監聽,客戶端通過網絡連接到服務端,服務端接入連接。客戶端發包給服務端,服務端接收到包後進行解析,調用對應的處理程序進行處理,處理程序處理成功後,修改數據並保存下來,再把響應包封包發送給客戶端。

**簡單的數據存儲可以保存在文件裏。**當用戶量逐漸增加,數據存儲的性能、完整和安全要求逐漸增大,數據存儲改爲存儲到數據庫。這樣服務端就分爲服務進程、數據庫進程兩個進程。服務進程跟數據庫進程通信,進行數據讀寫。由於遊戲的實時要求比較高,如果每個請求都要讀寫數據庫,當大量用戶同時訪問時,數據庫讀寫成了服務性能的瓶頸。因此,一般遊戲都做緩存,簡單的緩存可以緩存在服務進程的內存裏,業務處理直接操作緩存數據,每隔一段時間(例如 10 分鐘),用個獨立線程把被修改的緩存數據保存到數據庫。

這樣會有一個問題,就是一旦服務進程宕機,在保存間隔時間裏的遊戲數據就會丟失掉。特別是遊戲服務進程有更新上線時,穩定性還沒有被線上併發驗證,宕機的幾率會增加,數據丟失的風險也會增加。爲了減輕風險,可以考慮把數據緩存跟服務進程分離。使用共享內存,或者使用第三方的專業緩存程序(memcached、redis)作緩存。這樣數據丟失的風險就大大降低了。

隨着遊戲業務開發,遊戲業務不再是簡單的請求、返回,它還有一系列推送需求,如聊天、郵件系統,這類需求可能是全員推送,這會佔用服務進程的 CPU 時間,導致請求響應變慢。而且這種推送時效性要求沒有遊戲其他業務高,因此可以把這種廣播類型的推送抽離出來放到一個單獨的廣播進程,服務進程通過向廣播進程發送廣播消息到廣播隊列,廣播進程從廣播隊列獲取消息進行廣播。這樣又產生了新的問題,客戶端需要連接到兩個服務端的進程。爲了解決這個問題,可以增加網關進程,客戶端只需要連接到網關進程,由網關進程代替客戶端將請求發送到服務進程進行處理,服務進程、廣播進程的響應 / 推送進入網關,由網關確定轉發給哪個客戶端連接。

這樣服務端就變成了 5 個獨立進程的進程組,接下來對數據庫和數據緩存做下技術選型,可以選擇業界用得比較多的 mysql 數據庫、redis 緩存。利用 redis 的 list 結構做消息隊列建立起服務進程和廣播進程的通信。

這些進程都部署在一臺機器上,所以目前服務端的處理能力受限於一臺機器的硬件性能。當然我們也可以將這 5 個進程分散到多臺機器上,如果玩家人數再增加,可以把網關進程、服務進程和廣播進程集羣,redis、mysql 改成分佈式緩存和分佈式數據庫,這樣也能擴容。更通用的做法是對遊戲分服,每個分服的數據互相隔離。這是目前市面上大多數遊戲的做法,滾服運營也已經是比較成熟的運營手段。由這 5 個進程組成的進程組,1 個分服對應一個進程組,一個進程組部署在同一臺機器上。這樣通過分服就可以橫向支持更多的玩家併發訪問。

這樣又出現了新的問題,客戶端怎麼知道該連接哪個分服。而且現在的遊戲,同一個玩家可以在不同的分服進行遊戲,以達到好友同玩一個服。一般的,在玩家進入分服前,讓玩家先進行全局登錄,然後根據遊戲類型,要麼下發分服列表由玩家自己選擇分服,要麼給玩家指定分服(例如類似 COC 的所謂不分服遊戲,這個分服可以理解爲一個節點)。增加一個全局的賬號進程,負責遊戲的全局登錄、下發分服信息。賬號進程還負責支付回調,進行訂單確認和發貨。

隨着遊戲運營推廣,分服數量越來越多,遊戲迭代也需要對服務進行維護,靠人工維護分服列表顯得越來越笨拙。應該建立一個自動化的機制,當開新分服,或者分服進入維護的時候,自動更新分服列表。在這裏可以通過服務註冊發現的機制來實現自動化。利用一個全局的 redis 作爲註冊中心,所有分服的信息通過服務編號作爲字段,都保存在 redis 的一個 hash 結構裏,key 爲 gamesrv。當開新分服,或者切換分服狀態時,分服的服務進程向 redis 更新自己所在分服的信息,然後通過訂閱發佈機制,發佈 gamesrv 通道的消息,參數爲更新的服務編號。賬號進程在玩家全局登錄時,只需要把 redis 裏 key 爲 gamesrv 的 hash 返回給客戶端就可以。

還有一種情況是,分服宕機了,這個時候需要修改分服狀態爲維護中,避免玩家進入這個分服。而且 redis 裏分服的信息也應該落地固化,這樣就算 redis 重啓了也不丟失。這裏可以利用 redis 的固化機制保存數據。增加一個管理進程,redis 數據有變更就調用一次 redis 的 bgsave 命令。當然也可以搭建一個註冊進程,提供類似 redis 的訂閱發佈機制,以及 zookeeper 的連接監聽、樹形數據保存、落地固化。管理進程與所有分服的服務進程保持連接,當分服宕機時,管理進程知道對應分服的連接斷開,則修改 redis 裏對應分服的信息狀態爲維護中,這樣管理進程也起到監控作用。管理進程除了參與服務的註冊發現,還提供對服務和玩家進行管理和操作的服務,管理進程與賬號進程保持連接,賬號進程支付確認後可以通過管理進程通知具體的分服進行虛擬道具發貨。

賬號進程處理全局登錄,那就要有一個賬號的數據庫用來保存玩家的賬號信息,分服可以通過管理進程把分服創角、角色更新信息保存到賬號數據庫,以便玩家選擇分服時知道各分服角色的信息。玩家進行全局登錄是短暫的一次請求,所以用 http 短連接來增加訪問量。賬號進程支持集羣,這樣需要有對外統一的訪問地址。管理進程也提供一些 http 請求給管理後臺網頁對服務和玩家進行管理和操作。增加一個 http 網關提供統一的地址訪問,可以用業界普遍使用的 nginx 作爲 http 網關。

對於輕中度遊戲,遊戲的通信量不會很多,沒必要每個分服都有一個長連接 socket 網關。假設一個分服同時連接服務器的客戶端有 5k,一臺機器的 socket 網關能支持 5w 個玩家。這樣可以把網關獨立出來,一臺機器部署兩個 socket 網關,每個 socket 網關都可以進入任意一個分服。這樣子可以通過加減機器橫向伸縮 socket 網關,把網絡流量成本集中在幾臺網關機器上降低成本。這樣子賬號進程就需要知道網關列表和負載情況,以通過負載均衡給玩家返回合適的連接網關。每個網關也要監聽各個分服的狀態變化,確定是否連接對應的分服的服務進程、廣播進程。因此網關需要參與服務的註冊發現。

這樣一個服務端的架構就基本出來了。從圖中可以看出,最大單點風險是管理進程。可以通過守護進程讓管理進程崩潰自動重啓(守護進程作爲父進程創建、等待作爲子進程的管理進程退出後,如果沒有維護標記則重新創建子進程),提高整個服務的健壯性。

產品上線後還要有數據統計後臺,數據來源可以通過賬號進程、服務進程運行時生成日誌到日誌文件,由腳本分析日誌上報給統計數據庫。統計後臺可以作爲一個單獨的項目去做建表、上報、生成報表。

服務端架構就介紹到這裏,接下來聊一下網絡通信。

來源:

https://www.cnblogs.com/niudanshui/p/15294398.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7555R8qwUbVrBAME_pWDBg