1. 程式人生 > >Go遊戲服務端框架從零搭建(一)— 架構設計

Go遊戲服務端框架從零搭建(一)— 架構設計

         五邑隱俠,本名關健昌,10年遊戲生涯,現隱居海邊。

  本教程以Go語言分割槽遊戲服務端框架搭建為例。

  Go語言是Google開發的一種靜態強型別、編譯型、併發型、具有垃圾回收功能的程式語言。語法上近似C語言,支援介面、可通過struct包含另一個struct方式實現繼承等面向物件的概念。效能上媲美C/C++,相比C/C++更健壯,更易開發併發程式。我以前也寫C++服務端,接觸Go後,更傾向用Go做遊戲服務端開發。

  所謂分割槽遊戲,指遊戲將分為很多個區,不同區之間玩家不能互動或只有少量互動。玩家進入遊戲需要選擇分割槽,進入指定分割槽進行遊戲,一個玩家可以同時在不同分割槽有角色。目前市面上大多中重度網路遊戲都採用這種模式,分割槽遊戲適合不需要大DAU互動的遊戲,如卡牌、MMORPG、SLG等。從技術層面,分割槽屬於叢集擴容的一種手段;運營上,有利於分割槽精細運營,滾服運營已經是比較成熟的遊戲運營手段。相對分割槽遊戲,也存在不分割槽的社交遊戲,這類遊戲核心玩法是匹配對抗,例如COC。如果把分割槽遊戲的分割槽,對映為該類遊戲的節點服,廣播服對映為匹配服,對不分割槽遊戲的架構也就很好理解。

  本教程主要講分割槽遊戲服務端框架搭建。總體設計如下:

  分割槽遊戲,玩家先登入遊戲,然後進入指定分割槽。所以首先要有一個登入伺服器,提供全域性的登入服務。登入伺服器的核心資料是玩家賬號,核心業務是對玩家進行登入校驗,包括使用自有賬號系統、第三方賬號登入。賬號資料需要落地長期儲存,所以在該伺服器配套一個MySQL資料庫,用於儲存玩家賬號資訊。有一到多個登入服務程序,進行遊戲登入校驗。登入服務是一個短連線服務,使用http協議,對外通過nginx提供統一訪問地址,對多個登入服務程序做負載均衡。通常,登入介面還會返回分割槽資訊、玩家各分割槽角色簡要資訊、客戶端最新版本號、配置版本、資源版本、公告等,這些資訊可以通過gm服務(後面會介紹)動態更新,登入服在響應請求時獲取並返回給客戶端。所以在登入伺服器會配套一個redis服務,用於這些資料的快取。

  玩家登入遊戲成功後,將進入選定遊戲分割槽。分割槽是一組服務程序。大部分遊戲分割槽都採用長連線通訊,如果同時兼顧現在熱門的h5遊戲、微信小遊戲,考慮選用websocket做通訊(以前使用socket)。考慮到遊戲中會有廣播的需求,廣播資料和分割槽遊戲資料都希望從同一個連線返回給客戶端,有必要提供一個統一的分割槽入口閘道器服,閘道器服程序對客戶端提供統一的分割槽地址和埠,對內做資料轉發。分割槽的邏輯業務可以集中放在一個遊戲服程序裡。以前一些遊戲,遊戲服線上資料會儲存在程序記憶體裡,由於遊戲資料變化太頻繁,MySQL 頻繁讀寫效能不高,所以會隔一定時間才儲存到MySQL。但分割槽內遊戲服是一個單點,一旦崩掉,遊戲服的記憶體資料就會丟失,回檔到上一次儲存的時間。後來一些遊戲為了減少這種風險,把線上資料儲存到共享記憶體,再定時儲存到MySQL。共享記憶體依賴系統和語言,目前發現Go沒有直接支援。再後來有了memcached和redis,部分遊戲選擇用這種快取系統做快取,遊戲服崩掉,資料還在快取裡不會丟失,可以快速啟動遊戲服恢復服務。我選用redis作為快取,快取活躍玩家資料。隔一定時間,把變化資料儲存到MySQL。redis資料主要使用key-value方式儲存資料,每次業務處理都需要讀取、解析,再使用。對業務開發不是很友好。遊戲服程序記憶體還是會儲存線上玩家資料,玩家進入分割槽時從redis讀取到遊戲服記憶體,redis不命中則釋出訊息給資料服進行資料預熱,預熱成功後從redis讀取。後面的請求可以直接通過記憶體資料做業務判斷、處理,更改資料以事務方式儲存到redis,成功後響應給客戶端。這樣記憶體資料跟redis資料一致,而且可以把玩家資料拆成更細的單元,減少跟redis間的通訊。玩家下線後清除遊戲服記憶體資料。所以分割槽內配套一個redis、一個MySQL。為了建立定時儲存資料這個機制,且不會因遊戲服崩潰而受影響,配備一個功能很簡單的資料服,通過redis的釋出訂閱機制、訊息佇列,負責資料的定時落地固化、玩家註冊、資料預熱。

  前面提到廣播服,廣播服顧名思義主要負責廣播,例如跑馬燈廣播、世界聊天、世界boss。廣播服通過各個分割槽的閘道器服將資料廣播給玩家,因此廣播服將連線各個分割槽閘道器。廣播任務通過訊息佇列進行快取,這樣每個分割槽的廣播操作在寫到佇列後就可以響應客戶端。訊息佇列採用redis實現。廣播服是一個全域性的服務,為了避免單點風險,可以做成主從,通過redis的訂閱釋出機制,啟動時訂閱redis,如果一定時間沒有收到釋出訊息,認為主服務不存在,切換為主服務,取消訊息訂閱,連線各分割槽閘道器,定時向redis釋出訊息報活。

  除了業務相關的服務,需要對整個服務體系提供管理。例如開服、停服、更新配置/資源版本、發郵件、發公告、發放道具、踢人等。提供一個全域性的gm服,各分割槽服務啟動後,遊戲服程序連線到gm服並保持心跳,以通知gm服開/停服。gm服將這些變更資訊更新到登入服的redis,這樣玩家登入遊戲就知道各個服的狀態。gm服還可以通過向redis發訊息通知登入服進行封號等操作。由於各個遊戲服都連線到gm服,這樣就可以對各個分割槽發gm命令。gm服可以通過向廣播服的訊息佇列寫訊息發全員廣播。gm服的功能由運營人員進行操作,所以需要提供http服務,方便在網頁上訪問。gm服有道具發放的功能,所以第三方支付回撥可以通過gm服的http介面請求發貨。

  為了給運營提供決策,還需要提供統計後臺,對遊戲資料日誌進行收集、統計。由於登入服、各分割槽的遊戲服、gm服都會上報資料,資料來源廣,資料量大,需要做訊息佇列。因此登入服、遊戲服、gm服都通過redis的訊息佇列進行上報。統計服從redis讀取訊息,儲存資料日誌到MySQL。因此需要配套一個redis、一個MySQL。統計服的功能由運營人員使用,需要提供http服務,方便在網頁上訪問。統計服的http介面還支援客戶端進行資料上報。

  為了合併運營人員的頁面,gm服、統計服通過nginx提供統一的http地址。

  這樣就得到了如前面設計圖的整個服務框架。 

  本篇介紹到這裡,接下來會詳細介紹各個服務的實現。在此之前,下一篇先介紹一些通用的基礎機制設計和實現。