伺服器結構
我們把觀察點先集中在一個大區內。在大多數情況下,一個大區內都會有多組遊戲服,也就是多個遊戲世界可供選擇。簡單點來實現,我們完全可以拋棄這個大區的概念,認為一個大區也就是放在同一個機房的多臺伺服器組,各伺服器組間沒有什麼關係。這樣,我們可為每組伺服器單獨配備一臺登入服。最後的結構圖應該像這樣:
loginServer gameServer| /
|/ client
該結構下的玩家操作流程為,先選擇大區,再選擇大區下的某臺伺服器,即某個遊戲世界,點選進入後開始帳號驗證過程,驗證成功則進入了該遊戲世界。但是,如果玩家想要切換遊戲世界,他只能先退出當前遊戲世界,然後進入新的遊戲世界重新進行帳號驗證。
早期的遊戲大都採用的是這種結構,有些遊戲在實現時採用了一些技術手段使得在切換遊戲服時不需要再次驗證帳號,但整體結構還是未做改變。
該結構存在一個伺服器資源配置的問題。因為登入服處理的邏輯相對來說比較簡單,就是將玩家提交的帳號和密碼送到資料庫進行驗證,和生成會話金鑰傳送給遊戲服和客戶端,操作完成後連線就會立即斷開,而且玩家在以後的遊戲過程中不會再與登入服打任何交道。這樣處理短連線的過程使得系統在大多數情況下都是比較空閒的,但是在某些時候,由於請求比較密集,比如開新服的時候,登入服的負載又會比較大,甚至會處理不過來。
另外在實際的遊戲運營中,有些遊戲世界很火爆,而有些遊戲世界卻非常冷清,甚至沒有多少人玩的情況也是很常見的。所以,我們能否更合理地配置登入服資源,使得整個大區內的登入服可以共享就成了下一步改進的目標。
伺服器結構探討 -- 登入服的負載均衡
回想一下我們在玩wow時的操作流程:執行wow.exe進入遊戲後,首先就會要求我們輸入使用者名稱和密碼進行驗證,驗證成功後才會出來遊戲世界列表,之後是排隊進入遊戲世界,開始遊戲...
可以看到跟前面的描述有個很明顯的不同,那就是要先驗證帳號再選擇遊戲世界。這種結構也就使得登入服不是固定配備給個遊戲世界,而是全區共有的。
我們可以試著從實際需求的角度來考慮一下這個問題。正如我們之前所描述過的那樣,登入服在大多數情況下都是比較空閒的,也許我們的一個擁有20個遊戲世界的大區僅僅使用10臺或更少的登入服即可滿足需求。而當在開新區的時候,或許要配備40臺登入服才能應付那如潮水般湧入的玩家登入請求。所以,登入服在設計上應該能滿足這種動態增刪的需求,我們可以在任何時候為大區增加或減少登入服的部署。
當然,在這裡也不會存在要求新增太多登入服的情況。還是拿開新區的情況來說,即使新增加登入服滿足了玩家登入的請求,遊戲世界服的承載能力依然有限,玩家一樣只能在排隊系統中等待,或者是進入到遊戲世界中導致大家都卡。
另外,當我們在增加或移除登入服的時候不應該需要對遊戲世界服有所改動,也不會要求重啟世界服,當然也不應該要求客戶端有什麼更新或者修改,一切都是在背後自動完成。
最後,有關資料持久化的問題也在這裡考慮一下。一般來說,使用現有的商業資料庫系統比自己手工技術先進要明智得多。我們需要持久化的資料有玩家的帳號及密碼,玩家建立的角色相關資訊,另外還有一些遊戲世界全域性共有資料也需要持久化。
好了,需求已經提出來了,現在來考慮如何將其實現。
對於負載均衡來說,已有了成熟的解決方案。一般最常用,也最簡單部署的應該是基於DNS的負載均衡系統了,其通過在DNS中為一個域名配置多個IP地址來實現。最新的DNS服務已實現了根據伺服器系統狀態來實現的動態負載均衡,也就是實現了真正意義上的負載均衡,這樣也就有效地解決了當某臺登入服當機後,DNS伺服器不能立即做出反應的問題。當然,如果找不到這樣的解決方案,自己從頭打造一個也並不難。而且,通過DNS來實現的負載均衡已經包含了所做的修改對登入服及客戶端的透明。
而對於資料庫的應用,在這種結構下,登入服及遊戲世界服都會需要連線資料庫。從資料庫伺服器的部署上來說,可以將帳號和角色資料都放在一箇中心資料庫中,也可分為兩個不同的庫分別來處理,基到從物理上分到兩臺不同的伺服器上去也行。
但是對於不同的遊戲世界來說,其角色及遊戲內資料都是互相獨立的,所以一般情況下也就為每個遊戲世界單獨配備一臺資料庫伺服器,以減輕資料庫的壓力。所以,整體的伺服器結構應該是一個大區有一臺帳號資料庫伺服器,所有的登入服都連線到這裡。而每個遊戲世界都有自己的遊戲資料庫伺服器,只允許本遊戲世界內的伺服器連線。
最後,我們的伺服器結構就像這樣:
大區伺服器 /| /
/ |/ 登入服1登入服2世界服1世界服2/| ||
/ || |帳號資料庫 DBS DBS
這裡既然討論到了大區及帳號資料庫,所以順帶也說一下關於啟用大區的概念。wow中一共有八個大區,我們想要進入某個大區遊戲之前,必須到官網上啟用這個區,這是為什麼呢?
一般來說,在各個大區帳號資料庫之上還有一個總的帳號資料庫,我們可以稱它為中心資料庫。比如我們在官網上註冊了一個帳號,這時帳號資料是隻儲存在中心資料庫上的。而當我們要到一區去建立角色開始遊戲的時候,在一區的帳號資料庫中並沒有我們的帳號資料,所以,我們必須先到官網上做一次啟用操作。這個啟用的過程也就是從中心庫上把我們的帳號資料拷貝到所要到的大區帳號資料庫中。
伺服器結構探討 -- 簡單的世界服實現
討論了這麼久我們一直都還沒有進入遊戲世界伺服器內部,現在就讓我們來窺探一下里面的結構吧。
對於現在大多數MMORPG來說,遊戲伺服器要處理的基本邏輯有移動、聊天、技能、物品、任務和生物等,另外還有地圖管理與訊息廣播來對其他高階功能做支撐。如縱隊、好友、公會、戰場和副本等,這些都是通過基本邏輯功能組合或擴充套件而成。
在所有這些基礎邏輯中,與我們要討論的伺服器結構關係最緊密的當屬地圖管理方式。決定了地圖的管理方式也就決定了我們的伺服器結構,我們仍然先從最簡單的實現方式開始說起。
回想一下我們曾戰鬥過無數個夜晚的暗黑破壞神,整個暗黑的世界被分為了若干個獨立的小地圖,當我們在地圖間穿越時,一般都要經過一個叫做傳送門的裝置。世界中有些地圖間雖然在地理上是直接相連的,但我們發現其遊戲內部的邏輯卻是完全隔離的。可以這樣認為,一塊地圖就是一個獨立的資料處理單元。
既然如此,我們就把每塊地圖都當作是一臺獨立的伺服器,他提供了在這塊地圖上游戲時的所有邏輯功能,至於內部結構如何劃分我們暫不理會,先把他當作一個黑盒子吧。
當兩個人合作做一件事時,我們可以以對等的關係相互協商著來做,而且一般也都不會有什麼問題。當人數增加到三個時,我們對等的合作關係可能會有些複雜,因為我們每個人都同時要與另兩個人合作協商。正如俗語所說的那樣,三個和尚可能會碰到沒水喝的情況。當人數繼續增加,情況就變得不那麼簡單了,我們得需要一個管理者來對我們的工作進行分工、協調。遊戲的地圖伺服器之間也是這麼回事。
一般來說,我們的遊戲世界不可能會只有一塊或者兩塊小地圖,那順理成章的,也就需要一個地圖管理者。先稱它為遊戲世界的中心伺服器吧,畢竟是管理者嘛,大家都以它為中心。
中心伺服器主要維護一張地圖ID到地圖伺服器地址的對映表。當我們要進入某張地圖時,會從中心服上取得該地圖的IP和port告訴客戶端,客戶端主動去連線,這樣進入他想要去的遊戲地圖。在整個遊戲過程中,客戶端始終只會與一臺地圖伺服器保持連線,當要切換地圖的時候,在獲取到新地圖的地址後,會先與當前地圖斷開連線,再進入新的地圖,這樣保證玩家資料在伺服器上只有一份。
我們來看看結構圖是怎樣的: 中心伺服器 // /// /登入服 地圖1地圖2地圖n / |/ // |/ /客戶端
很簡單,不是嗎。但是簡單並不表示功能上會有什麼損失,簡單也更不能表示遊戲不能賺錢。早期不少遊戲也確實採用的就是這種簡單結構。
伺服器結構探討 -- 繼續世界服
都已經看出來了,這種每切換一次地圖就要重新連線伺服器的方式實在是不夠優雅,而且在實際遊戲運營中也發現,地圖切換導致的卡號,複製裝備等問題非常多,這裡完全就是一個事故多發地段,如何避免這種頻繁的連線操作呢?
最直接的方法就是把那個圖倒轉過來就行了。客戶端只需要連線到中心服上,所有到地圖伺服器的資料都由中心服來轉發。很完美的解決方案,不是嗎?
這種結構在實際的部署中也遇到了一些挑戰。對於一般的MMORPG伺服器來說,單臺伺服器的承載量平均在2000左右,如果你的伺服器很不幸地只能帶1000人,沒關係,不少遊戲都是如此;如果你的伺服器上跑了3000多玩家依然比較流暢,那你可以自豪地告訴你的策劃,多設計些大量消耗伺服器資源的玩法吧,比如大型國戰、公會戰爭等。
2000人,似乎我們的策劃朋友們不大願意接受這個數字。我們將地圖伺服器分開來原來也是想將負載分開,以多帶些客戶端,現在要所有的連線都從中心服上轉發,那連線數又遇到單臺伺服器的可最大承載量的瓶頸了。
這裡有必要再解釋下這個數字。我知道,有人一定會說,才帶2000人,那是你水平不行,我隨便寫個TCP伺服器都可帶個五六千連線。問題恰恰在於你是隨便寫的,而MMORPG的伺服器是複雜設計的。如果一個演示socket API用的echo伺服器就能滿足MMOG伺服器的需求,那寫伺服器該是件多麼愜意的事啊。
但我們所遇到的事實是,伺服器收到一個移動包後,要向周圍所有人廣播,而不是echo伺服器那樣簡單的迴應;伺服器在收到一個連線斷開通知時要向很多人通知玩家退出事件,並將該玩家的資料寫入資料庫,而不是echo伺服器那樣什麼都不需要做;伺服器在收到一個物品使用請求包後要做一系列的邏輯判斷以檢查玩家有沒有作弊;伺服器上還啟動著很多定時器用來更新遊戲世界的各種狀態......
其實這麼一比較,我們也看出資源消耗的所在了:伺服器上大量的複雜的邏輯處理。再回過頭來看看我們想要實現的結構,我們既想要有一個唯一的入口,使得客戶端不用頻繁改變連線,又希望這個唯一入口的負載不會太大,以致於接受不了多少連線。
仔細看一看這個需求,我們想要的僅僅只是一臺管理連線的伺服器,並不打算讓他承擔太多的遊戲邏輯。既然如此,那五六千個連線也還有滿足我們的要求。至少在現在來說,一個遊戲世界內,也就是一組伺服器內同時有五六千個線上的玩家還是件讓人很興奮的事。事實上,在大多數遊戲的大部分時間裡,這個數字也是很讓人眼紅的。
什麼?你說夢幻、魔獸還有史先生的那個什麼征途遠不止這麼點人了!噢,我說的是大多數,是大多數,不包括那些明星。你知道大陸現在有多少遊戲在運營嗎?或許你又該說,我們不該在一開始就把自己的目標定的太低!好吧,我們還是先不談這個。
繼續我們的結構討論。一般來說,我們把這臺負責連線管理的伺服器稱為閘道器伺服器,因為內部的資料都要通過這個閘道器才能出去,不過從這臺伺服器提供的功能來看,稱其為反向代理伺服器可能更合適。我們也不在這個名字上糾纏了,就按大家通用的叫法,還是稱他為閘道器伺服器吧。
閘道器之後的結構我們依然可以採用之前描述的方案,只是,似乎並沒有必要為每一個地圖都開一個獨立的監聽埠了。我們可以試著對地圖進行一些劃分,由一個Master Server來管理一些更小的Zone Server,玩家通過閘道器連線到Master Server上,而實際與地圖有關的邏輯是分派給更小的Zone Server去處理。
最後的結構看起來大概是這樣的:
Zone Server Zone Server
/ /
/ /
Master Server Master Server
/ / /
/ / /
Gateway Server / /
| / / /
| / / /
| Center Server
|
|
Client
伺服器結構探討 -- 最終的結構
如果我們就此打住,可能馬上就會有人要嗤之以鼻了,就這點古董級的技術也敢出來現。好吧,我們還是把之前留下的問題拿出來解決掉吧。
一般來說,當某一部分能力達不到我們的要求時,最簡單的解決方法就是在此多投入一點資源。既然想要更多的連線數,那就再加一臺閘道器伺服器吧。新增加了閘道器服後需要在大區服上做相應的支援,或者再簡單點,有一臺主要的閘道器服,當其負載較高時,主動將新到達的連線重定向到其他閘道器服上。
而對於遊戲服來說,有一臺還是多臺閘道器服是沒有什麼區別的。每個代表客戶端玩家的物件內部都保留一個代表其連線的物件,訊息廣播時要求每個玩家物件使用自己的連線物件傳送資料即可,至於連線是在什麼地方,那是完全透明的。當然,這只是一種簡單的實現,也是普通使用的一種方案,如果後期想對訊息廣播做一些優化的話,那可能才需要多考慮一下。
既然說到了優化,我們也稍稍考慮一下現在結構下可能採用的優化方案。
首先是當前的Zone Server要做的事情太多了,以至於他都處理不了多少連線。這其中最消耗系統資源的當屬生物的AI處理了,尤其是那些複雜的尋路演算法,所以我們可以考慮把這部分AI邏輯獨立出來,由一臺單獨的AI伺服器來承擔。
然後,我們可以試著把一些與地圖資料無關的公共邏輯放到Master Server上去實現,這樣Zone Server上只保留了與地圖資料緊密相關的邏輯,如生物管理,玩家移動和狀態更新等。
還有聊天處理邏輯,這部分與遊戲邏輯沒有任何關聯,我們也完全可以將其獨立出來,放到一臺單獨的聊天伺服器上去實現。
最後是資料庫了,為了減輕資料庫的壓力,提高資料請求的響應速度,我們可以在資料庫之前建立一個數據庫快取伺服器,將一些常用資料快取在此,伺服器與資料庫的通訊都要通過這臺伺服器進行代理。快取的資料會定時的寫入到後臺資料庫中。
好了,做完這些優化我們的伺服器結構大體也就定的差不多了,暫且也不再繼續深入,更細化的內容等到各個部分實現的時候再探討。
好比我們去看一場晚會,
舞臺上演員們按著預定的節目單有序地上演著,但這就是整場晚會的全部嗎?顯然不止,在幕後還有太多太多的人在忙碌著,甚至在晚會前和晚會後都有。我們的遊戲伺服器也如此。
在之前描述的部分就如同舞臺上的演員,是我們能直接看到的,幕後的工作人員我們也來認識一下。
現實中有警察來維護秩序,遊戲中也如此,這就是我們常說的GM。GM可以採用跟普通玩家一樣的拉入方式來進入遊戲,當然許可權會比普通玩家高一些,也可以提供一臺GM伺服器專門用來處理GM命令,這樣可以有更高的安全性,GM服一般接在中心伺服器上。
在以時間收費的遊戲中,我們還需要一臺計費的伺服器,這臺伺服器一般接在閘道器伺服器上,註冊玩家登入和退出事件以記錄玩家的遊戲時間。
任何為使用者提供服務的地方都會有日誌記錄,遊戲伺服器當然也不例外。從記錄玩家登入的時間,地址,機器資訊到遊戲過程中的每一項操作都可以作為日誌記錄下來,以備查錯及資料探勘用。至於蒐集玩家機器資料所涉及到的法律問題不是我們該考慮的。
差不多就這麼多了吧,接下來我們會按照這個大致的結構來詳細討論各部分的實現。
伺服器結構探討 -- 一點雜談
再強調一下,伺服器結構本無所謂好壞,只有是否適合自己。我們在前面探討了一些在現在的遊戲中見到過的結構,並盡我所知地分析了各自存在的一些問題和可以做的一些改進,希望其中沒有謬誤,如果能給大家也帶來些啟發那自然更好。
突然發現自己一旦羅嗦起來還真是沒完沒了。接下來先說說我在開發中遇到過的一些困惑和一基礎問題探討吧,這些問題可能有人與我一樣,也曾遇到過,或者正在被困擾中,而所要探討的這些基礎問題向來也是爭論比較多的,我們也不評價其中的好與壞,只做簡單的描述。
首先是伺服器作業系統,linux與windows之爭隨處可見,其實在大多數情況下這不是我們所能決定的,似乎各大公司也基本都有了自己的傳統,如網易的freebsd,騰訊的linux等。如果真有權利去選擇的話,選自己最熟悉的吧。
決定了OS也就基本上確定了網路IO模型,windows上的IOCP和linux下的epool,或者直接使用現有的網路框架,如ACE和asio等,其他還有些商業的網路庫在國內的使用好像沒有見到,不符合中國國情嘛。:)
然後是網路協議的選擇,以前的選擇大多傾向於UDP,為了可靠傳輸一般自己都會在上面實現一層封裝,而現在更普通的是直接採用本身就很可靠的TCP,或者TCP與UDP的混用。早期選擇UDP的主要原因還是頻寬限制,現在寬頻普通的情況下TCP比UDP多出來的一點點開銷與開發的便利性相比已經不算什麼了。當然,如果已有了成熟的可靠UDP庫,那也可以繼續使用著。
還有訊息包格式的定義,這個曾在雲風的blog上展開過激烈的爭論。訊息包格式定義包括三段,包長、訊息碼和包體,爭論的焦點在於應該是訊息碼在前還是包長在前,我們也把這個當作是信仰問題吧,有興趣的去雲風的blog上看看,論論。
另外早期有些遊戲的包格式定義是以特殊字元作分隔的,這樣一個好處是其中某個包出現錯誤後我們的遊戲還能繼續。但實際上,我覺得這是完全沒有必要的,真要出現這樣的錯誤,直接斷開這個客戶端的連線可能更安全。而且,以特殊字元做分隔的訊息包定義還加大了一點點網路資料量。
最後是一個純技術問題,有關socket連線數的最大限制。開始學習網路程式設計的時候我犯過這樣的錯誤,以為port的定義為unsigned short,所以想當然的認為伺服器的最大連線數為65535,這會是一個硬性的限制。而實際上,一個socket描述符在windows上的定義是unsigned int,因此要有限制那也是四十多億,放心好了。
在伺服器上port是監聽用的,想象這樣一種情況,web server在80埠上監聽,當一個連線到來時,系統會為這個連線分配一個socket控制代碼,同時與其在80埠上進行通訊;當另一個連線到來時,伺服器仍然在80埠與之通訊,只是分配的socket控制代碼不一樣。這個socket控制代碼才是描述每個連線的唯一標識。按windows網路程式設計第二版上的說法,這個上限值配置影響。
一種高效能網路遊戲伺服器架構設計
伺服器端則負責響應所有客戶端的連線請求和遊戲邏輯處理,並控制所有客戶端的遊戲畫面繪製。客戶端與伺服器通過網路資料包互動完成每一步遊戲邏輯,由於遊戲邏輯是由伺服器負責處理的,要保證面對海量使用者登入時,遊戲具有良好的流暢性和使用者體驗,優秀的伺服器架構起到了關鍵的作用。
1.1 伺服器架構分類
伺服器組的架構一般分為兩種:第一種是帶閘道器伺服器的伺服器架構;第二種是不帶閘道器伺服器的伺服器架構,這兩種方案各有利弊。在給出伺服器架構設計之前,先對這兩種設計方案進行詳細的探討。
所謂閘道器伺服器,其實是 Gate伺服器 ,比如LoginGate、GameGate等。閘道器伺服器的主要職責是將客戶端和遊戲伺服器隔離,客戶端程式直接與這些閘道器伺服器通訊,並不需要知道具體的遊戲伺服器內部架構,包括它們的IP、埠、網路通訊模型(完成埠或Epoll)等。客戶端只與閘道器伺服器相連,通過閘道器伺服器轉發資料包間接地與遊戲伺服器互動。同樣地,遊戲伺服器也不直接和客戶端通訊,發給客戶端的協議都通過閘道器伺服器進行轉發。
1.2 伺服器架構設計
根據網路遊戲的規模和設計的不同,每組伺服器中伺服器種類和數量是不盡相同的。本文設計出的帶閘道器伺服器的伺服器組架構如圖1所示。

本文將伺服器設計成帶閘道器伺服器的架構,雖然加大了伺服器的設計複雜度,但卻帶來了以下幾點好處:
(1)作為網路 通訊的中轉站 ,負責維護將內網和外網隔離開,使外部無法直接訪問內部伺服器,保障內網伺服器的安全,一定程度上較少外掛的攻擊。
(2)閘道器伺服器負責 解析資料包、加解密、超時處理和一定邏輯處理 ,這樣可以提前過濾掉錯誤包和非法資料包。
(3)客戶端程式只需建立與閘道器伺服器的連線即可進入遊戲,無需與其它遊戲伺服器同時建立多條連線,節省了客戶端和伺服器程式的網路資源開銷。
(4)在玩家跳伺服器時,不需要斷開與閘道器伺服器的連線,玩家資料在不同遊戲伺服器間的切換是內網切換,切換工作瞬間完成,玩家幾乎察覺不到,這保證了遊戲的流暢性和良好的使用者體驗。
在享受閘道器伺服器帶來上述好處的同時,還需注意以下可能導致負面效果的兩個情況: 如何避免閘道器伺服器成為高負載情況下的通訊瓶頸問題以及由於閘道器的單節點故障導致整組伺服器無法對外提供服務的問題 。上述兩個問題可以採用“多閘道器” 技術加以解決。顧名思義,“多閘道器” 就是同時存在多個閘道器伺服器,比如一組伺服器可以配置三臺GameGate。當負載較大時,可以通過增加閘道器伺服器來增加閘道器的總體通訊流量,當一臺閘道器伺服器宕機時,它只會影響連線到本伺服器的客戶端,其它客戶端不會受到任何影響。
從圖1的伺服器架構圖可以看出,一組伺服器包括LoginGate、LoginServer、GameGate、GameServer、DBServer和MServer等多種伺服器。LoginGate和GameGate就是閘道器伺服器,一般一組伺服器會配置3臺GameGate,因為穩定性對於網路遊戲運營來說是至關重要的,而伺服器宕機等突發事件是遊戲運營中所面臨的潛在風險,配置多臺伺服器可以有效地降低單個伺服器宕機帶來的風險。另外,配置多臺閘道器伺服器也是進行負載均衡的有效手段之一。下面將對各種伺服器的主要功能和彼此之間的資料互動做詳細解釋。
(1)LoginGate
LoginGate主要負責在玩家登入時 維護客戶端與LoginServer之間的網路連線與通訊 ,對LoginServer和客戶端的通訊資料進行加解密、校驗。
(2)LoginServer
LoginServer主要功能是驗證玩家的 賬號是否合法 ,只有通過驗證的賬號才能登入遊戲。從架構圖可以看出, DBServer和GameServer會連線LoginServer。玩家登入基本流程是,客戶端傳送賬號和密碼到LoginServer驗證,如果驗證通過,LoginServer會給玩家分配一個SessionKey,LoginServer會把這個SessionKey傳送給客戶端、DBServer和GameServer,在後續的選擇角色以後進入遊戲過程中,DBServer和GameServer將驗證SessionKey合法性,如果和客戶端攜帶的SessionKey不一致,將無法成功獲取到角色或者進入遊戲。
(3)GameGate
GameGate(GG)主要負責在使用者遊戲過程中負責 維持GS與客戶端之間的網路連線和通訊 ,對GS和客戶端的通訊資料進行加解密和校驗,對客戶端發往GS的使用者資料進行解析,過濾錯誤包,對客戶端發來的一些協議作簡單的邏輯處理,其中包括遊戲邏輯中的一些超時判斷。在使用者選擇角色過程中負責維持DBServer與客戶端之間的網路連線和通訊,對DBServer和客戶端的通訊資料進行加解密和校驗,對客戶端發往DBServer的使用者資料做簡單的分析。維持客戶端與MServer之間的網路連線與通訊、加解密、資料轉發和簡單的邏輯處理等。
(4)GameServer
GameServer(GS)主要負責 遊戲邏輯處理 。網路遊戲有龐大世界觀背景,絢麗激烈的陣營對抗以及完備的裝備和技能體系。目前,網路遊戲主要包括任務系統、聲望系統、玩家PK、寵物系統、擺攤系統、行會系統、排名系統、副本系統、生產系統和寶石系統等。從軟體架構角度來看,這些系統可以看著GS的子系統或模組,它們共同處理整個遊戲世界邏輯的運算。遊戲邏輯包括角色進入與退出遊戲、跳GS以及各種邏輯動作(比如行走、跑動、說話和攻擊等)。
由於整個遊戲世界有許多遊戲場景,在該架構中一組伺服器有3臺GS共同負責遊戲邏輯處理,每臺遊戲伺服器負責一部分地圖的處理,這樣不僅降低了單臺伺服器的負載,而且降低了GS宕機帶來的風險。玩家角色資訊裡會保持玩家上次退出遊戲時的地圖編號和所在GS編號,這樣玩家再次登入時,會進入到上次退出時的GS。
上面提到過,在驗證賬號之後,LoginServer會把這個SessionKey 發給GS,當玩家選擇角色登入GS時,會把SessionKey一起發給GS,這時GS會驗證SessionKey是否與其儲存的相一致,不一致的話GS會拒絕玩家進入遊戲。MServer的主要負責GS之間的資料轉發以及資料廣播,另外,一些系統也可以放到MServer上,這樣也可以減輕GS的運算壓力。
(5)DBServer
DBServer主要的功能是 快取玩家角色資料 ,保證角色資料能快速的讀取和儲存。由於角色資料量是比較大的,包括玩家的等級、經驗、生命值、魔法值、裝備、技能、好友、公會等。如果每次GS獲取角色資料都去讀資料庫,效率必然非常低下,用DBServer快取角色資料之後,極大地提高了資料請求的響應速度。
LoginServer會在玩家選組時把SessionKey發給DBServer,當玩家傳送獲取角色資訊協議時會帶上這個SessionKey,如果跟DBServer儲存的SessionKey不一致,則DBServer會認為玩家不是合法使用者,獲取角色協議將會失敗。另外,玩家選取角色正式進入遊戲時,GS會給DBServer傳送攜帶SessionKey的獲取角色資訊協議,這時DBServer同樣會驗證SessionKey的合法性。總之,只有客戶端、DBServer和GS所儲存的SessionKey一致,才能保證協議收到成功反饋。
與DBServer通訊的伺服器主要有GG,GS和LoginServer,DBServer與GG互動的協議主要包括列角色、建立角色、刪除角色、恢復角色等,DBServer與GS互動的協議包括讀取角色資料、儲存角色資料和跳伺服器等,DBServer與LoginServer互動的協議主要是使用者登入協議,這時候會給DBServer傳送SessionKey。
(6)MServer
每一個組有一臺MServer,主要 負責維持3臺GS之間資料的轉發和資料廣播 。另外一些遊戲系統也可能會放到MServer上處理,比如行會系統。
1.3 伺服器互動的主要流程
下面給出伺服器之間資料通訊的主要流程從這些流程能看出各種伺服器之間是如何資料互動和協同工作的。

圖2的流程說明了,在選角色過程中,客戶端會把攜帶遊戲賬號和SessionKey的選角色協議發給GG,GG做一些簡單處理之後轉發給DBServer,DBServer要驗證SessionKey的合法性,驗證通過之後,DBServer會從角色資訊緩衝區裡取出該賬戶的所有角色資訊發給客戶端。這個過程在客戶端的表現是,當選擇好伺服器組之後,客戶端會直接顯示該賬號下的所有角色,之後就可以選擇角色進入遊戲了。

圖3的流程說明了,在玩家選角色正式進入遊戲時,客戶端會把攜帶遊戲賬號、角色ID和SessionKey的登入協議發給GG,GG做一些簡單處理之後轉發給GS。GS會驗證SessionKey的合法性,驗證通過之後,GS會把驗證通過的結果發給客戶端,同時GS給DBServer發獲取角色資料的協議,這些角色資料是一個玩家所有的遊戲資料,包括裝備、技能等等。

圖4的流程說明了,在玩家遊戲過程,客戶端把邏輯協議(包括走、說話、跑、使用技能等)發給GG,GG完成加解密和簡單邏輯處理之後轉發給GS,GS負責這些協議的主要
邏輯處理。
2 總結
網路遊戲伺服器的架構設計已經成為當前網路遊戲研究領域的熱點,因為 高效能伺服器架構設計 是一款網路遊戲成功的關鍵。本文從實際應用出發,提出了一種高效能的伺服器架構設計解決方案,並且詳細探討了各種伺服器的功能,本文的最後給出了幾個伺服器之間資料通訊的關鍵流程,以圖文並茂的方式解釋各個伺服器是如何協同工作的。
遊戲伺服器架構系列 - 閘道器服務
繼上一篇介紹了的分散式遊戲伺服器架構,後面的課程我們將對於架構中的每一種服務和具體技術細節進行詳細介紹。
首先回顧下游戲伺服器架構中的列出的服務,包括:
閘道器伺服器
中心伺服器
單區伺服器
跨區伺服器
映象伺服器
今天我們來介紹遊戲伺服器架構中至關重要的服務: 閘道器伺服器 。
服務描述: 即用於維持玩家客戶端的連線,將玩家發的遊戲請求轉發到具體後端服務的伺服器。
功能特性:
1. 對外開放:即客戶端需要知道閘道器的IP和埠,才能連線上來;
2. 統一入口:架構中可能存在很多後端服務,如果沒有一個統一入口,則客戶端需要知道每個後端服務的IP和埠。
3. 請求轉發:由於統一了入口,所以閘道器必須能將客戶端的請求轉發到準確的服務上。
4. 無感更新:由於玩家連線的是閘道器伺服器,只要連線不斷;更新後端伺服器對玩家來說是無感知的,或者感知很少(根據實現方式不同)。
一般情況下,有了以上4個特性,這個閘道器就可以用了。
但是如果只有上面4個特性,我們用Nginx做為閘道器也是可以的,為什麼還需要自己做閘道器?
因為我們的遊戲閘道器還需要具備以下特殊功能:
特殊功能:
1. Session認證:即能維護客戶端是否登入成功的狀態,對於未登入的請求,不予以轉發,從而預防惡意攻擊。
2. 流量限流:遊戲經常會遇到DDOS攻擊,一個客戶端可以通過一個for迴圈一直給你發請求,所以必須進行限制。
3. 踢下線:遊戲維護時,為了讓玩家能更新補丁,會將玩家踢下線,重新走一遍登入流程,避免客戶端與服務端的資料不一致,造成顯示上的BUG。此外客服也需要經常對一些違規的玩家進行踢下線處理。
4. 線上統計:為什麼閘道器來做線上統計呢?因為只有它有所有的玩家連線資訊,所以可以輕鬆統計當前有多少玩家線上。
5. 協議加密:為了避免客戶端的惡意攻擊,我們需要將請求進行加密,由於統一了入口,所以閘道器來做非常容易。
6. 心跳檢測:用於檢測客戶端是否已經掉線,一般超過幾分鐘沒有收到心跳請求,則認為客戶端已經掉線,直接請求登入資料,讓玩家重新走登入流程。
整合以上的功能後,便形成了以下閘道器服務架構圖:

閘道器服務架構圖
這張圖中的路由控制,會根據不同遊戲型別會有所變動,其中:
1. 後端服務路由表:維護了後端當前有哪些服務註冊到網關了,以及服務對應哪些區服的配置資訊。
2. 區服登錄檔:維護了當前開了哪些區服及區服資訊。
3. 終端管理:所有連線上閘道器的裝置或程序都被認為是一個終端,終端會有一個編號,這個編號對應後端服務編號或者玩家編號。當需要轉發訊息給後端服務或客戶端時,就需要從終端管理裡找到具體的連線進行訊息傳送。
接下來用圖示介紹一下,客戶端、閘道器伺服器、後端伺服器的互動流程:

客戶端與閘道器、後端服務的互動流程
Step1:客戶端連線閘道器伺服器,然後傳送登入請求給閘道器;
Step2:閘道器直接將登入請求轉發給對應區服的後端伺服器進行登入驗證;
Step3:後端伺服器驗證成功後,會返回登入資訊給閘道器;
Step4:閘道器會根據登入資訊維持一個Session對映,用於安全驗證和重登判斷,然後轉發登入資訊給客戶端;
Step5:客戶端收到登入成功訊息後,就可以傳送業務請求給網關了;
Step6;閘道器收到業務請求後,會先判斷玩家是否登入過,登入過的才轉發給後端伺服器,並且在協議頭增加玩家標記;
Step7:後端伺服器收到業務請求後,根據協議頭的玩家標記,找到玩家的資料進行業務處理,然後返回給閘道器;
Step8:閘道器收到業務回覆後,直接返回給客戶端;
PS: 上面涉及到的一些關鍵技術細節,如:流量限流、協議加密,將會在後續的文章中詳細介紹演算法。
遊戲伺服器架構系列 - 閘道器限流
ofollow,noindex">MaxwellGames 關注
2018.10.30 10:00* 字數 830 閱讀 150評論 0喜歡 3
為什麼要進行閘道器限流?
在前面我們介紹的遊戲服務端架構中,客戶端通過Socket連線直連閘道器,所有請求都需要經過閘道器,然後由閘道器統一進行轉發,為了避免玩家的DDOS攻擊,所以需要在閘道器進行限流。
常見的演算法主要有計數器限流、令牌桶限流和漏桶限流,這些演算法都是單機的演算法,正好可以用在閘道器限流。
演算法
1、計數器限流
嚴格意義上來說計數器限流不屬於限流演算法,使用計數器來進行限流,主要用來限制總併發數,比如資料庫連線數;只要全域性總請求數或者一定時間段的總請求數設定的閥值則進行限流,是簡單粗暴的總數量限流,而不是平均速率限流。
2、令牌桶演算法(Token Bucket)
令牌桶演算法是一個存放固定容量令牌的桶,按照固定速率往桶裡新增令牌。令牌桶演算法的描述如下:
假設限制1秒鐘生成2個令牌,則按照500毫秒的固定速率往桶中新增令牌;
桶中最多存放a個令牌,當桶滿時,新新增的令牌被丟棄或拒絕;
當有n個請求到達,將從桶中刪除n個令牌,接著請求被髮送到網路上;
如果桶中的令牌不足n個,則不會刪除令牌,該請求將被限流(要麼丟棄,要麼緩衝區等待)。

令牌桶演算法(圖片來自網路)
3、漏桶演算法(Leaky Bucket)
漏桶演算法是一個存放固定水滴的桶,按照固定速率流出水滴,可以用於流量整形和流量控制,漏桶演算法的描述如下:
一個固定容量的漏桶,按照常量固定速率流出水滴;
如果桶是空的,則不需流出水滴;
可以以任意速率流入水滴到漏桶;
如果流入水滴超出了桶的容量,則流入的水滴溢位了(被丟棄,要麼緩衝區等待),而漏桶容量是不變的。

漏桶演算法(圖片來自網路)
對比
令牌桶是按照固定速率往桶中新增令牌,請求是否被處理需要看桶中令牌是否足夠,當令牌數減為零時則拒絕新的請求;漏桶是按照固定速率從桶中流出請求,當流入的請求數累積到漏桶容量時,則新流入的請求被拒絕
令牌桶限制的是平均流入速率,允許突發請求,只要有令牌就可以處理,支援一次拿5個令牌,6個令牌,並允許一定程度突發流量;漏桶限制的是常量流出速率,即流出速率是一個固定常量值,比如都是2的速率流出,而不能一次是2,下次又是3,從而平滑突發流入速率
基於Golang的演算法實現: https://github.com/MaxwellBackend/Games/tree/master/ratelimit
遊戲伺服器架構系列 - 閘道器協議加密
MaxwellGames 關注
2018.11.05 14:58 字數 734 閱讀 123評論 0喜歡 1
一、概要
閘道器在遊戲伺服器架構中充當著很重要的角色,根據不同型別專案承擔的功能也不一樣,主要的功能有以下幾種:
1. 壓縮: 壓縮是一件比較耗時操作,放在閘道器可以減輕遊戲壓力;
2. 過濾: 過濾主要是識別非正常請求,保護後端服務;
3. 轉發: 轉發是保證客戶端訊息準確快速到達目標地址,加密來提高客戶端與伺服器之間通訊安全;
4. 加密: 這篇文章主要介紹一下游戲加密相關的技術。
二、加密演算法的分類
加密主要提高破解通訊協議的難度,避免協議被識別篡改,加密的技術有很多,大致可以分為以下幾種:
1. 可逆加密
1.1 對稱加密:DES、AES、RC4等;
1.2 非對稱加密 :RSA等;
2. 不可逆加密:如:md5等;
三、加密流程
遊戲閘道器通訊協議採用可逆加密,使客戶端可以解密協議,讓客戶端與伺服器可以安全通訊,加密的一般流程如下
1. 握手階段,基於RSA演算法
2. 交換KEY,基於DH演算法
3. 資料流加密與解密,可採用:AES,RC4等演算法
握手階段(RSA)
客戶端與伺服器握手階段主要是驗證伺服器真偽,這一步可以用RSA來實現(可以省略)
1. 伺服器隨機一段串A
2. 務器用客戶端公鑰加密傳送客戶端
3. 客戶端收到後用客戶端私鑰解密出串A
4. 客戶端用伺服器公鑰加密串B和解密後的串A,傳送給伺服器
5. 伺服器收到後用伺服器私鑰解密串A和之前伺服器生成的對比驗證,ok則握手成功
交換KEY(DH)
1. 客戶端通過Diffie-Hellman演算法生成伺服器加密,解密種子(伺服器加密種子是客戶端解密種子,伺服器解密種子是客戶端加密種子),
2. 伺服器通過Diffie-Hellman演算法根據加密,解密種子生成加密,解密KEY,客戶端加密,解密種子
3. 客戶端根據加密,解密種子生成KEY
資料流加密與解密
用協商過的KEY,進行資料流加密與解密(aes,rc4)
可以根據需要選擇不同的加密演算法,安全性高的可以選用AES,資料長度不變的可以選用RC4,
AES和RC4在所有演算法中都是比較高效的演算法,RC4效率好於AES,但安全不如AES,
對AES,DES,RC4幾種演算法使用案例及效能對比
Github實現: https://github.com/MaxwellBackend/Games/tree/master/security