1. 程式人生 > 其它 >go語言遊戲服務端開發(一)——架構

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

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

五邑隱俠,本名關健昌,12年遊戲生涯。 本教程以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