游戲服務端架構發展史(上)
類型1:卡牌,跑酷等弱交互服務端
卡牌跑酷類因為交互弱,玩家和玩家之間不需要實時面對面PK,打一下對方的離線數據,計算下排行榜,買賣下道具即可,所以實現往往使用簡單的 HTTP服務器:
登錄時可以使用非對稱加密(RSA, DH),服務器根據客戶端uid,當前時間戳還有服務端私鑰,計算哈希得到的加密 key 并發送給客戶端。之后雙方都用 HTTP通信,并用那個key進行RC4加密。客戶端收到key和時間戳后保存在內存,用于之后通信,服務端不需要保存 key,因為每次都可以根據客戶端傳上來的 uid 和 時間戳 以及服務端自己的私鑰計算得到。用模仿 TLS的行為,來保證多次 HTTP請求間的客戶端身份,并通過時間戳保證同一人兩次登錄密鑰不同。
每局開始時,訪問一下,請求一下關卡數據,玩完了又提交一下,驗算一下是否合法,獲得什么獎勵,數據庫用單臺 MySQL或者 MongoDB即可,后端的 Redis做緩存(可選)。如果要實現通知,那么讓客戶端定時15秒輪詢一下服務器,如果有消息就取下來,如果沒消息可以逐步放長輪詢時間,比如30秒; 如果有消息,就縮短輪詢時間到10秒,5秒,即便兩人聊天,延遲也能自適應。
此類服務器用來實現一款三國類策略或者卡牌及酷跑的游戲已經綽綽有余,這類游戲因為邏輯簡單,玩家之間交互不強,使用 HTTP來開發的話,開發速度快,調試只需要一個瀏覽器就可以把邏輯調試清楚了。
類型2:第一代游戲服務器 1978
1978年,英國著名的財經學校University of Essex的學生 Roy Trubshaw編寫了世界上第一個MUD程序《MUD1》,在University of Essex于1980年接入 ARPANET之后加入了不少外部的玩家,甚至包括國外的玩家。《MUD1》程序的源代碼在 ARPANET共享之后出現了眾多的改編版本,至此MUD才在全世界廣泛流行起來。不斷完善的 MUD1的基礎上產生了開源的 MudOS(1991),成為眾多網游的鼻祖:
MUDOS采用 C語言開發,因為玩家和玩家之間有比較強的交互(聊天,交易,PK),MUDOS使用單線程無阻塞套接字來服務所有玩家,所有玩家的請求都發到同一個線程 去處理,主線程每隔1秒鐘更新一次所有對象(網絡收發,更新對象狀態機,處理超時,刷新地圖,刷新NPC)。
游戲世界采用房間的形式組織起來,每個房間有東南西北四個方向可以移動到下一個房間,由于歐美最早的網游都是地牢迷宮形式的,因此場景的基本單位 被成為 “房間”。MUDOS使用一門稱為LPC的腳本語言來描述整個世界(包括房間拓撲,配置,NPC,以及各種劇情)。游戲里面的高級玩家(巫師),可以不斷 的通過修改腳本來為游戲添加房間以及增加劇情。早年 MUD1上線時只有17個房間,Roy Trubshaw畢業以后交給他的師弟 Richard Battle,在 Richard Battle手上,不斷的添加各種玩法到一百多個房間,終于讓 MUD發揚光大。
用戶使用 Telnet之類的客戶端用 Tcp協議連接到 MUDOS上,使用純文字進行游戲,每條指令用回車進行分割。比如 1995年國內第一款 MUD游戲《俠客行》,你敲入:”go east”,游戲就會提示你:“后花園 – 這里是歸云莊的后花園,種滿了花草,幾個莊丁正在澆花。此地乃是含羞草生長之地。這里唯一的出口是 north。這里有:花待 阿牧(A mu),還有二位莊丁(Zhuang Ding)”,然后你繼續用文字操作,查看阿牧的信息:“look a mu”,系統提示:“花待 阿牧(A mu)他是陸乘風的弟子,受命在此看管含羞草。他看起來三十多歲,生得眉清目秀,端正大方,一表人才。他的武藝看上去【不是很高】,出手似乎【極輕】”。 然后你可以選擇擊敗他獲得含羞草,但是你吃了含羞草卻又可能會中毒死亡。在早期網上資源貧乏的時候,這樣的游戲有很強的代入感。
用戶數據保存在文件中,每個用戶登錄時,從文本文件里把用戶的數據全部加載進來,操作全部在內存里面進行,無需馬上刷回磁盤。用戶退出了,或者每 隔5分鐘檢查到數據改動了,都會保存會磁盤。這樣的系統在當時每臺服務器承載個4000人同時游戲,不是特別大的問題。從1991年的 MUDOS發布后,全球各地都在為他改進,擴充,退出新版本,隨著 Windows圖形機能的增強。1997游戲《UO》在 MUDOS的基礎上為角色增加的x,y坐標,為每個房間增加了地圖,并且為每個角色增加了動畫,形成了第一代的圖形網絡游戲。
因為游戲內容基本可以通過 LPC腳本進行定制,所以MUDOS也成為名副其實的第一款服務端引擎,引擎一次性開發出來,然后制作不同游戲內容。后續國內的《萬王之王》等游戲,很多 都是跟《UO》一樣,直接在 MUDOS上進行二次開發,加入房間的地圖還有角色的坐標等要素,該架構一直為國內的第一代 MMORPG提供了穩固的支持,直到 2003年,還有游戲基于 MUDOS開發。
雖然后面圖形化增加了很多東西,但是這些MMORPG后端的本質還是 MUDOS。隨著游戲內容的越來越復雜,架構變得越來越吃不消了,各種負載問題慢慢浮上水面,于是有了我們的第二代游戲服務器。
類型3:第二代游戲服務器 2003
2000年后,網游已經脫離最初的文字MUD,進入全面圖形化年代。最先承受不住的其實是很多小文件,用戶上下線,頻繁的讀取寫入用戶數據,導致 負載越來越大。隨著在線人數的增加和游戲數據的增加,服務器變得不抗重負。同時早期 EXT磁盤分區比較脆弱,稍微停電,容易發生大面積數據丟失。因此第一步就是拆分文件存儲到數據庫去:
此時游戲服務端已經脫離陳舊的 MUDOS體系,各個公司在參考 MUDOS結構的情況下,開始自己用 C在重新開發自己的游戲服務端。并且腳本也拋棄了 LPC,采用擴展性更好的 Python或者 Lua來代替。由于主邏輯使用單線程模型,隨著游戲內容的增加,傳統單服務器的結構進一步成為瓶頸。于是有人開始拆分游戲世界,變為下面的模型:
游戲服務器壓力拆分后得意緩解,但是兩臺游戲服務器同時訪問數據庫,大量重復訪問,大量數據交換,使得數據庫成為下一個瓶頸。于是形成了數據庫前 端代理(DB Proxy),游戲服務器不直接訪問數據庫而是訪問代理,再有代理訪問數據庫,同時提供內存級別的cache。早年 MySQL4之前沒有提供存儲過程,這個前端代理一般和 MySQL跑在同一臺上,它轉化游戲服務器發過來的高級數據操作指令,拆分成具體的數據庫操作,一定程度上代替了存儲過程:
但是這樣的結構并沒有持續太長時間,因為玩家切換場景經常要切換連接,中間的狀態容易錯亂。而且游戲服務器多了以后,相互之間數據交互又會變得比 較麻煩,于是人們拆分了網絡功能,獨立出一個網關服務 Gate(有的地方叫 Session,有的地方叫 LinkSvr之類的,名字不同而已):
但是這樣的結構并沒有持續太長時間,因為玩家切換場景經常要切換連接,中間的狀態容易錯亂。而且游戲服務器多了以后,相互之間數據交互又會變得比 較麻煩,于是人們拆分了網絡功能,獨立出一個網關服務 Gate(有的地方叫 Session,有的地方叫 LinkSvr之類的,名字不同而已):
把網絡功能單獨提取出來,讓用戶統一去連接一個網關服務器,再有網關服務器轉發數據到后端游戲服務器。而游戲服務器之間數據交換也統一連接到網管 進行交換。這樣類型的服務器基本能穩定的為玩家提供游戲服務,一臺網關服務1-2萬人,后面的游戲服務器每臺服務5k-1w,依游戲類型和復雜度不同而 已,圖中隱藏了很多不重要的服務器,如登錄和管理。這是目前應用最廣的一個模型,到今天任然很多新項目會才用這樣的結構來搭建。
人都是有慣性的,按照先前的經驗,似乎把 MUDOS拆分的越開性能越好。于是大家繼續想,網關可以拆分呀,基礎服務如聊天交易,可以拆分呀,還可以提供web接口,數據庫可以拆分呀,于是有了下面的模型:
這樣的模型好用么?確實有成功游戲使用類似這樣的架構,并且發揮了它的性能優勢,比如一些大型 MMORPG。但是有兩個挑戰:每增加一級服務器,狀態機復雜度可能會翻倍,導致研發和找bug的成本上升;并且對開發組挑戰比較大,一旦項目時間吃緊, 開發人員經驗不足,很容易弄掛。
比如我見過某上海一線游戲公司的一個 RPG上來就要上這樣的架構,我看了下他們團隊成員的經驗,問了下他們的上線日期,勸他們用前面稍微簡單一點的模型。人家自信得很,認為有成功項目是這么 做的,他們也要這么做,自己很想實現一套。于是他們義無反顧的開始編碼,項目做了一年多,然后,就沒有然后了。
現今在游戲成功率不高的情況下,一開始上一套比較復雜的架構需要考慮投資回報率,比如你的游戲上線半年內 PCU會去到多少?如果一個 APRG游戲,每組服務器5千人都到不了的話,那么選擇一套更為貼近實際情況的結構更為經濟。即使后面你的項目真的超過5千人朝著1萬人目標奔的話,相信 那個時候你的項目已經掙大錢了 ,你數著錢加著班去逐步迭代,一次次拆分它,相信心里也是樂開花的。
上面這些類型基本都是從拆分 MUDOS開始,將 MUDOS中的各個部件從單機一步步拆成分布式。雖然今天任然很多新項目在用上面某一種類似的結構。因為他們本質上都是對 MUDOS的分解,故將他們歸納為第二代游戲服務器。