遊戲伺服器如何實現全球同服
剛從東家離職,得知之前設計的服務端架構被阿里和ucloud推薦,看起來不怎麼普及解決方案,本著開源精神分享一下思路吧。
既然面對全球用戶,必然是要做到服務的動態拓展,自動化部署,歸根結底還是如何改造成分散式伺服器,老話題了。
伺服器的不同業務分別在不同模塊運行應該是共識吧,要是所有業務堆在一起,無法讓核心業務實現分散式,也就失去全球服的意義。
不同模塊的通訊協議定製,簡單說就不同業務分布在不同進程,他們之間的通訊能在區域網範圍內也能彼此連接。
然後要做的是業務最大程度解耦。比如郵件業務壓力比較小,只需要一台伺服器就能滿足需求,而戰鬥伺服器在同樣量級需要兩台伺服器,如果這兩個業務放在一起就不太合理了。
如此帶來的問題是,面對繁多的業務類型如何部署。我能想到的方案是所有服務都打包進docker,啟動時加入環境變數來區分不同服務。
關於服務發現,有現成的開源方案consul+registrator,consul為核心,registrator監聽docker.sock文件將新啟動的服務註冊到consul內,docker.sock內有container啟動參數,這樣獲取類型版本都能實現了,具體實現官方都有,這裡就不介紹了。
這樣通過consul就能獲得所有的服務類型以及每個服務類型下的所有地址和埠。
解耦完業務部分以後,遊戲存儲的分散式也有現成的解決方案,最新版本的redis已經有cluster集群模式,這裡需要在redis前端加一層代理用來計算key值的分布,隨著slot增加,redis會重新分配所有key,而訪問一個slot時,key在另一個slot會返回所在slot節點的地址,如果在業務層反覆嘗試讀取用戶數據並不合理,在所有slot前面寫一層代理計算key值所在slot保存業務層到資料庫的一次通訊可以得到結果,redis本身是開源的,他的key值分布的演算法可以參考,github也有代理項目,自行搜索。
之後必須要考慮的是所有服務的緩存問題,如果一個業務節點有用戶緩存,用戶訪問到這個業務的另一個節點就無法保證數據一致,因此在做業務設計時最大限度保證服務無狀態,如果某個服務必須有狀態可以在某個kv存儲服務存下一個用戶對應服務節點信息,在服務集群前面再加一層代理解析用戶信息獲取節點信息再做轉發,目前nginx已經支持tcp連接轉發,還有haproxy也能用。代理需要做的是解開第一個包找到節點再做轉發。這裡有個小trick,登錄服務如果作為一個代理轉發到業務服務可以控制根據客戶端發來的信息做不同版本區分從而實現業務伺服器的灰度更新。
總結下來所謂的全球方案只是解耦和服務發現,根據需求使用不同工具,具體的坑還要踩,整體方案還是挺靠譜的,畢竟之前設計的架構已經成為成功案例了,呵呵噠,但是不論架構多好都救不了業務出現問題。架構層面能做的就是這些,當然要真正做到全球同服還要服務提供商的支持,如何保證全球玩家連接通一伺服器的延遲可以不影響遊戲等等。全球服的架構帶來的不僅僅是可以將所有玩家集中到一起,也可以降低伺服器成本,運維成本,維護成本。當然也可以基於這樣的架構做分服,只是把不同服的玩家分開處理業務,可以說是能夠兼容分服,業務層面可以完全實現。
推薦閱讀:
※請問開一個冒險島私服需要花多錢?
※伺服器後台開發,下面的路怎麼走?
※在集群中部署多個伺服器如何解決SESSION問題?
※遊戲伺服器架構通識