這篇blog題目涉及的範圍真大!以致於在這裏須要先寫一篇前言把範圍縮小。選擇寫這樣一個系列的文章,主要是想給工做了兩年的本身一個交代,或者說是一個階段性的總結。兩年時間裏,房價依然再漲,工資依然跑不贏CPI,某人依然在仰望星空。期間不少夢碎了,不少還在堅持着,生活過得波瀾不驚。而我也從剛畢業是的青澀逐步蛻變爲「老油條」。不知道是一種悲哀、仍是一種悲哀、仍是一種悲哀....... 慶幸的是夢還在繼續,一顆倔強的心還在堅持。但願明天的明天被束縛的心能回到夢開始的地方!html
==========================我只是條分割線========================前端
做爲本系列blog的開篇前言,本文主要明確網絡遊戲服務器構架的設計目標,並做出一些限定。由於本系列所討論的服務器端構架只適用於部分網遊,並非一個通用的網遊服務器構架。java
設計目標:mysql
設計目標就這些多說無益,核心就是設計出可以支持相似魔獸世界的大型MMORPG遊戲的網遊服務器端。具體的設計以及設計時的取捨、須要解決的問題等,會在後續的文章進行詳細的介紹。程序員
本文並無涉及什麼逆向工程,只是拜讀刀劍Online服務器端主程的文章後[1],想結合本身的經驗談一談。web
PS:因爲題目範圍太大,本系列的前言作了一些限制。算法
1、網絡遊戲服務器sql
要想設計好網絡遊戲服務器的構架,首先須要知道網絡遊戲服務器在玩家遊戲過程當中發揮什麼做用。就我我的的理解:網遊服務器在玩家遊戲過程當中扮演上帝的角色。玩家在服務器制定的規則下進行遊戲,服務器負責同步在線玩家之間的屬性、操做、狀態等等,最終在多個不一樣的客戶端呈現一個「統一」的遊戲世界。數據庫
所謂的服務器構架在本系列blog中,主要是指如何將服務器各部分合理的安排,以實現最初的功能需求。好的結構不是一蹴而就的,是經過需求的推進一步步的完善。並且每一個設計者心中的標準不盡相同,因此我認爲並無絕對優秀服務器構架。本系列文章中所謂的優秀構架是指各方面達到一種平衡(包括成本等的非技術因素)。編程
下面先介紹刀劍Online的服務器構架(後續還可能有WOW、天龍等):
2、刀劍Online
圖1 刀劍Online服務器構架
看了像素的技術總監魏華的文章[1],感受有點意思。文章中所介紹的服務器構架並不複雜,但知足通常MMORPG網遊要求應該是綽綽有餘了。按照魏華本身的話,這樣的服務器構架主要知足可以接受如下幾條限制的網絡遊戲:
服務器包括遊戲中和遊戲外兩大部分,這裏主要討論遊戲中的服務器構架,相似客戶端自動更新等的遊戲外服務器不在本文的討論範圍。
接下來對刀劍Online的服務器構架的各個部分進行詳細的分析,其中包含不少我本身的想法,不少內容都是我猜想的,所以不能「信覺得真」。
2.1 鏈接負載服務器(Connection Load Server,CLS)
遊戲客戶端在遊戲過程當中其實是和鏈接負載服務器(簡稱CLS)進行鏈接並作數據交互的。如文中[1]所訴,CLS主要的做用是:
鏈接服務器的主要工做正如上述魏華談到的,但有幾點並無作出強調,下面本人結合平時的實際工做給出一些補充(不必定正確):
根據經驗:
CLS做爲與client創建鏈接、進行數據交互的「Gate」,從程序角度來看CLS的代碼應該是最簡潔高效的。由於CLS主要負責與客戶端交互數據是的加密解密、以及數據搬運。而使用流加密算法RC4對整個數據流進行加密解密是很耗CPU的,所以代碼的高效在這個模塊是十分的重要。
爲了提升程序的運行效率,CLS程序每每會使用-O3的選項進行編譯,這無形中又對代碼的編寫有更高的要求。CLS通常會有本身的打包機制(控制發送頻率),所以常會使用TCP_NODELAY選項禁用Nagle算法。
CLS常會被分配到不一樣的物理機器上,由於操做系統在處理TCP包時,須要經過軟中斷來通知進程或者喚醒system call,在服務器十分繁忙的時候CPU可能處理不過來。解決辦法是:使用多核的服務器,而後把TCP的軟中斷平均分配到多個CPU。(一些操做系統默認只使用0號CPU來處理,Fedora Core release 2默認就是隻使用0號CPU,較新的版本我沒有作研究)
CLS在作數據流的解密後,每每須要把數據包構形成內部服務器進程間通信使用的protocol,這種protocol模塊要獨立,序列化和反序列化的接口要穩定,這樣之後須要更換協議模塊也不至於傷筋動骨。可使用像google的protobuf這樣的開源協議,減小開發難度。
CLS負責創建和client的鏈接,多會使用多個CLS進程才能支撐1w的在線人數,所以在CLS前端通常會有負載均衡的程序,負責把創建鏈接的請求均勻的提交到各個CLS。
有一個須要討論的問題:做爲服務器端的「Gate」,只負責數據轉發的CLS是否須要對client發過來的數據進行徹底的解密?或者只解密包頭,知道轉發的目的地便可?(RC4並無增長流的長度,所以能夠只作部分解密)
CLS只作部分解密:
好處:將耗費CPU資源的解密功能分攤到別的進程;各進程各服務能夠在解密後用不一樣的方案來構造本身的protocol。
CLS作徹底解密:
好處:能夠提早過濾部分無效的消息,只作部分解密也能夠作提早過濾,可是這樣太過於依賴協議的設計;在CLS處作徹底解密,則日後服務器端的之間的消息傳遞都是明文,利於抓包查錯;因爲CLS的功能比較簡單,很容易經過加機器來進行擴展,所以計算放在CLS上是比較明智的選擇。
總結:
CLS是一個功能相對簡單但要求代碼簡潔高效的程序,在設計實現的時應該注重效率及代碼編寫規範。通過對比在CLS程序對數據流進行徹底解密是利大於弊的,推薦使用這種方案。
上一篇《網絡遊戲服務器構架設計(二)》介紹了刀劍Online的鏈接負載服務器CLS,博友提出質疑「說得不夠詳細,好比你怎麼,場景服務器怎麼纔算一個場景服務器,場景服務器切換怎麼處理不斷線後鏈接另外一個場景的,還有不少細節問題沒有說到」,本篇就來介紹遊戲服務器最爲核心的部分:遊戲邏輯服務器,同時也回答了這位博友的問題。
PS:本篇的文章結構主要分兩個部分,前半部分(2.2節)介紹刀劍Online如何實現遊戲邏輯服務器,後半部分(2.3節)爲本人結合實際工做對這套服務器構架作出的一些展開解釋及補充,主要對設計思想進行分析。精彩在後面哦!
-------------------------------------------我只是條分割線--------------------------------------------
先來回顧一下刀劍Online的整體構架圖:
2.2 遊戲邏輯服務器
顧名思義,就是和遊戲具體邏輯相關的服務器(這應是一個統稱)。這塊是網遊服務器端的核心部分,不一樣的遊戲差異會很大。在刀劍中,遊戲邏輯服務器分爲兩部分:總控服務器和場景服務器。
2.2.1 總控服務器(Master Server,MS)
關於總控服務器的做用,刀劍Online的主程是這麼解釋的:
總控服務器(如下簡稱MS)的做用之一是負責玩家在具體遊戲內容以外的操做(即.玩家進入場景服務器以前地操做)。如:登陸、註銷、各類角色操做(建立、刪除、選擇)等等。
MS和全部地場景服務器都保持鏈接,這樣它就成爲各個場景服務器間的樞紐,當須要一些跨場景服務器的操做或者須要訪問別的場景服務器數據的時候,指令都先發給MS,而後MS根據須要再轉發給相應地場景服務器或者直接發給相應的用戶,並進行後續地協調工做。
好比:在場景服務器1上的用戶A但願向遊戲中的用戶B發出一條添加好友的請求,則場景服務器1向MS發送添加好友指令並附帶了用戶B的名字,MS查找發現有B這樣的用戶,則直接把指令發給CLS,而後由CLS轉發給B用戶;若是沒有發現B用戶則直接通知A未發現B。
又好比:在場景服務器1上的用戶A點中了傳送點,將要傳到場景X,場景服務器1發現X場景並不在本身的管轄範圍內,因而發送轉移指令給MS,MS查找發現場景X在場景服務器2上,因而先發送用戶A的離開指令給場景服務器1,讓用戶退回到MS上,而後再發送用戶A的進入指令給場景服務器2,並說明用戶將要進入的場景爲X,這樣一次跨服務器的場景轉移就完成了。[1]
2.2.2 場景服務器(Zone Server,ZS)
關於場景服務器的做用,刀劍Online的主程是這麼解釋的:
場景服務器(如下簡稱ZS)就是具體負責遊戲場景的服務器。玩家選擇人物開始遊戲以後就進入了這種服務器(即開始遊戲以後CLS把全部玩家的操做指令都轉給ZS)。
玩家的各類操做的邏輯都是由ZS完成的,同時,ZS也要負責各個場景以及場景中的NPC和場景中各個物品的邏輯運行。
每款遊戲的真正遊戲性的核心就是這些ZS。它的具體細節我就不過多的講述了,各個遊戲的具體內容應該都不相同。不過有幾個原則是共同的:
1、是要高效。若是ZS對遊戲邏輯的處理效率低,會直接影響玩家同時在線的數量,並致使遊戲中的玩家感受很「卡」,這是除了網絡延時以外第二個會形成遊戲 「卡」的地方。提升效率的方法除了對代碼進行優化外,就是要使用高效的腳本系統,直接把腳本轉化爲程序代碼編譯到程序中去也不失爲一個辦法。
2、是要有災難恢復機制,就是當ZS發生非法操做時(只要不停電)可以恢復出非法操做時各個用戶的數據。這個在遊戲運營初期服務器尚不穩定的時候很是重要。雖然咱們也能夠經過加快用戶存盤間隔的方式(好比把每10分鐘存一次盤改成每1分鐘存一次盤),可是這會成倍加劇數據庫負擔,同時也不能避免因爲用戶恰好在存盤間隔的時候得到了重要的金錢或道具而致使丟失的狀況。刀劍採起的方法是在申請用戶關鍵數據對象的時候經過包裝的函數從共享內存中申請,這樣即使ZS非法操做了,共享內存並不會消失,在從新啓動的時候就能夠從共享內存中恢復出程序非法時的用戶數據。固然恢復的時候也須要對用戶數據進行一些校驗,以避免把已經被破壞的數據存入數據庫。[1]
2.3 設計思想分析
遊戲邏輯服務器主要負責彙總全部在線的client發來的各類操做、狀態等數據包,通過一系列的處理後有選擇的廣播給須要的client,從而給全部在線的玩家呈現一個「統一」的世界。
優秀的邏輯服務器架構須要優秀的設計思想,而優秀的設計思想又源於對遊戲虛擬世界的適度抽象。抽象能夠看做一個工程問題,同時也能夠看做一個哲學問題,這正是遊戲開發的魅力所在。本系列blog將圍繞這種抽象一步步展開,結合不一樣的項目來詮釋網絡遊戲服務器端的設計思想(但願能作到....)。
站在服務器端的角度對遊戲虛擬世界進行抽象,首先要弄清楚構造虛擬世界須要些什麼?讓咱們來想象一下吧(如下內容參照了《盜夢空間》-「Inception」),先來看一段視頻:
Inception是我很是喜歡的影片,第一次看到這一段的時候,就感受很是像遊戲設計。今天能把它寫下來也算沒浪費幾十塊的電影票錢。
影片中飾演the architect(造夢師)的艾倫·佩姬(女),正在接受萊昂納多(飾演the extractor-盜夢者)的訓練。萊昂納多說道:「Remember you are the dreamer you built this world。」,「I'm the subject my mind populates it」。值得關注的兩個名詞:world,subject。這就是咱們要討論的主題。那麼構造網絡遊戲的虛擬世界須要些什麼呢?其實萊昂納多已經替我回答了這個問題:「We create and perceive out world simultaneously. You create the world of the dream, We bring the subject into that dream, and they fill it with their subconscious.」。world就至關於遊戲裏的場景,而subject就是一個個在線的玩家(player)。
遊戲世界(world,這裏的world泛指遊戲世界及地圖,見2.3.2)及遊戲對象(object,包括player)是構造網絡遊戲服務器端時,須要關注的兩個重點。如何處理好world和object的關係和地位直接影響到服務器端的構架。
2.3.1 遊戲對象(object)
先來看遊戲裏通常會有那些object,以Mangos爲例:
圖2 mangos遊戲對象的class diagram[2]
對於上面的類層次結構圖這裏不作展開(留到本系列後面的文章中展開),這裏引用mangos的遊戲對象是想給讀者一個直觀的印象,遊戲中的object在服務器端是個什麼摸樣。類的層次是對遊戲世界中的對象抽象後獲得的結果,抽象須要「適度」:若是類的層次過深,維護起來困難,並且後期每每會致使基類過於臃腫、不堪重負;若是類的層次過淺,一些object的共性不能體現,不少代碼會重複出如今各個子類裏,複用性差。
2.3.2 遊戲世界(world)
遊戲世界在本文是泛指遊戲對象所在的場景,以及附加在場景之上的地圖管理和對象管理,這裏統稱遊戲世界管理。
如何進行遊戲世界的管理是一個複雜的工程問題,網絡遊戲經常會把整個遊戲世界分爲若干張地圖,每張地圖又會分爲若干個區域進行管理。這若干張地圖能無縫的過渡就稱爲無縫大地圖模式(相似WOW),若是像刀劍Online那樣只能經過傳送服務在兩張不一樣地圖之間穿梭的,稱爲有縫地圖。而相對來講服務器端會比客戶端簡單一些,例:若是在客戶端看到的場景是下面這個樣子:
服務器端根據劃分區域的不一樣可能看到的地圖是這個樣子:
這裏服務器端用到了tile-based的方式來管理地圖,這種tile的方式在2d遊戲中十分的常見(打格子),而不少3d網遊的服務器端爲了減小運算量也採用這種tile模式劃分區域進行管理。若是對地圖管理有興趣請閱讀雲風的blog《用四叉樹管理散佈在平面上的對象》、《碰撞檢測》,本文先不作詳細的展開。
2.3.3 遊戲對象和遊戲世界的關係
如何處理遊戲對象和遊戲世界的關係和地位,是影響服務器端架構的最直接因素。爲了體會到這點,下面將以刀劍Online爲例,分析玩家對象(player,遊戲對象中最爲重要的部分)和遊戲世界的關係對整個服務器構架設計的影響。
1. 玩家對象player的構建
刀劍Online把遊戲邏輯服務器分爲總控服務器(Master Server,MS)和場景服務器(Zone Server,ZS,本文提到的遊戲世界多指ZS),那麼在client成功登錄server後,服務器端應該在哪構建player對象呢?在MS上?仍是在ZS上?這是個工程問題,同時也是個哲學問題.......
刀劍Online應該採用的是第2中方式,把player放在Zone Server上。
2. 玩家對象player和world的關係
如何處理玩家對象player和world的關係集中體現了服務器端的哲學,值得細細品味。處理二者的關係在本人看來有兩種主要的模式:一種是以player的中心的「自我」模式;另外一種是以遊戲世界爲中心的「上帝之手」模式。這兩種模式的哲學主要體如今加鎖模式上,接下來進行詳細介紹:
(一)player的「自我」模式
「自我」模式顧名思義,就是player以自我爲中心,什麼事都要親歷親爲。以player使用物品爲例:
如上圖是一個完整的player使用物品的流程。「自我」模式體如今上圖的(1)位置,在(1)中首先取到服務器中對應的player,加鎖後用player調用本身的處理函數進行處理。因爲(1)至關於處理客戶端發來的請求的入口處,所以在這裏進行加鎖和解鎖操做是十分合適的,此後的(2)~(5)player在調用本身的處理函數時,編程人員徹底不須要考慮加鎖的問題。
所謂的「自我」模式,其實就是指在服務器端對player的操做其實都是player本身去完成的,「我」本身去把事情作完。處理邏輯時「我」(本身的player實例)不會主動去鎖住對方的player實例,所以一個player不能修改另外一個player的數據,world也不能修改player的數據。world和player的關係是「獨立」的,player身上記着world的信息(至關於「我屬於哪一個世界」),world裏存放着player的實例,可是它們直接不能直接修改對方的數據。這麼作是爲了不死鎖,使得加鎖解鎖變得簡單而統一(如上圖)。
「自我」模式有加鎖解鎖的便利,可是一個網絡遊戲怎麼可能player和player、player和world直接沒有交互呢?交互就須要得到或修改對方的數據,遇到這樣的問題「自我」模式怎麼處理呢?請看下圖:
如上圖:client A向服務器發送「向玩家B發起攻擊」的消息,服務器端client A對應的實例Player A收到消息後,發現須要與player B進行交互,根據「自我」模型的限定,player A不能直接修改player B的血量等信息,這時player A須要作的是將本身的信息打包成一個msg結構而後push_back到消息隊列message queue並指定player B做爲接收者。在message queue上將這個msg轉發給player B前,會先調用lock(playerB)將B鎖住,接着把msg傳給player B進行處理,在處理完畢後再調用unlock(playerB)。lock和unlock都在統一的地方調用,加鎖解鎖十分簡潔,playerB處理msg消息時不用關心任何加鎖問題。在上圖的(2)中,player B進行傷害計算後將傷害消息廣播給周圍的玩家。
注意:「自我」模式下,傷害計算是在受攻擊方進行的。player之間、player和world之間都是經過消息傳遞進行交互的。
總結:
優勢:「自我」模式的優勢集中體如今加鎖上,編程人員在編寫具體邏輯時不用擔憂加鎖問題,也不用費盡心思來避免死鎖,由於加鎖都在消息入口處統一作了,同時不會主動去對別的player實例進行加鎖操做。使用這種模式時,最好把移動的相關邏輯獨立出來使用不一樣的鎖,以避免照成過多的加鎖衝突和等待。
缺點:「自我」模式的缺點也是十分明顯的,每一個player實例做爲不一樣的「我」獨立存在,須要交互時只能經過消息隊列來傳遞信息,極大的增長了交流的成本(內存、CPU佔用率都會增長)。一個player每每不能即時的取到別的player的數據,這樣一來不少的計算都只能作延後處理,好比傷害計算就只能放在被攻擊者的player上,使得不少須要作先驗判斷的技能實現起來變得複雜,這類技能只能靠釋放技能的player,發送請求消息給其餘player,而後再由其餘player把本身的信息經過msg queue發給釋放技能的player。異步處理方式每每會但來更多的煩惱——須要增長不少錯誤判斷、錯誤處理以及超時處理等等。在線人數高的時候,message queue的容量以及所佔用的內存也是須要考慮的問題。
引用狄更斯的:「It was the best of times, it was the worst of times」。本人依葫蘆畫瓢:「自我」模式是一種nb的設計,也是一種sb的設計.......
(二)world的「上帝之手」模式
「上帝之手」模式是以world爲中心(相似war3),以地圖爲單位進行劃分,player、NPC、monster等遊戲對象都隸屬於world。在world的掌控之下,就像有一隻上帝之手在撥弄着這些小玩意。以client A向服務器發送「向玩家B發起攻擊」消息爲例,服務器端的處理流程以下:
從上圖中能夠看出「上帝之手」模式的核心是world去完成這項任務:找到playerA和B,把他們都鎖住,而後交給技能模塊來進行傷害計算,最後把結果廣播出去。整個過程就像在玩war3這樣的RTS遊戲,服務器就像一個神,以斜45度的上帝視角來觀察全部的玩家。
「上帝之手」模式的設計難點在於加鎖策略,由於須要對多個對象進行加鎖,加解鎖的順序不當容易產生死鎖。加鎖策略有不少種,這裏不作具體的展開。只介紹一種最經常使用的方法,即對加鎖對象進行排序。遊戲對象在產生時都會有一個惟一的id作標識,當lock ()函數能按照一種穩定的算法對這些id進行排序時,就能夠避免死鎖。好比對遊戲對象進行分類:player、npc、monster等,先對分類進行排序,再對對象的id進行排序。須要注意的是MMORPG遊戲常常須要進行合服操做,爲了保證合服後player的id不會重複,須要對player_id進行一些規劃。
總結:
優勢:服務器程序扮演上帝的角色,能夠得到幾乎全部的信息,這對邏輯編寫帶來極大的便利。和「自我」模式同樣,最好把移動的相關邏輯獨立出來使用不一樣的鎖,以避免照成過多的加鎖衝突和等待。「上帝之手」模式更容易進行總體規劃,更容易實現模塊化設計,下降程序的耦合度。
缺點:上帝也不是這麼好演的,「上帝之手」模式對總體框架、接口設計、加鎖策略等有更高的要求,同時對編程人員的要求也會更高。
最近閒着沒事把雲風的《開發筆記》看了個遍,但願能從大牛的開發軌跡中獲得一些啓發。但多是由於本人level過低,一遍看下來仍是雲裏霧裏,不甚明白。沒辦法只好再看一遍,但願能對他們的服務器端架構有個簡單的認識,這裏同時作些筆記。
PS:本文是我我的對雲風的開發筆記的讀後感,可能會有不少錯誤,慎入!
----------------------------------------華麗麗的分割線-------------------------------
1、服務器劃分原則
在現有的網絡遊戲服務器端架構中,可能是以功能和場景來劃分服務器結構的。負載均衡和集羣暫且不在本文中討論(bigworld、atlas)。服務器劃分能夠基於如下原則:
接下來咱們來看看雲風的服務器架構是如何處理好以上幾點的。
圖1 服務器架構(此圖爲本人猜想,可能有誤)
2、運行時的玩家數據
網絡遊戲服務器程序一項重要的工做就是根據client發過來的數據包,在服務器端模擬玩家的行爲操做並把這些行爲廣播出去。那麼服務器程序在運行時就須要一些實體來保存玩家的數據,這些實體能夠是一個類,也能夠是一個線程,設計思想不一樣採用的實體差異也會很大。這裏涉及服務器端設計的一個核心問題:運行時玩家數據的保存、修改及數據流向。
agent
雲風經過抽象實體agent來處理單個client的服務請求,agent和client是1:1的關係(見圖1)。agent是在gate程序後端,負責翻譯、轉發以及迴應客戶端發過來的請求。agent的主要工做內容見雲風的《開發筆記 (1)》。值得補充的是設計agent的主要優點是:
把對單個 client 服務的代碼集中寫在 agent 服務中。由 agent 再和內部其它服務溝通。數據加載使用共享內存的方案,由 agent 向持久化模塊發出信號,作加載或純盤處理,經過共享內存獲得結構化數據塊。[2]
agent至關於client在服務器上對應的實體,玩家的屬性和數據只能由agent來修改,別的服務只有讀權限。經過attach操做得到數據(attach多是經過服務器通信框架skynet,也有可能直接mmap到共享內存sharedb上以得到數據)。
agent的設計使得整個系統對玩家數據的修改只有一個輸入點,數據流十分的明確,易於維護。雖然這種設計可能會照成數據的屢次複製,可是帶來的代碼維護和查錯上的便利是十分可觀的。
若是把全部的agent放在同一個進程裏,在編程該程序時還應該考慮到容錯問題,好比說(1)使用C++編寫這個程序,agent以類的形式存在,使用thread pool來處理收到的數據包,實際操做時thread的數量是會遠遠小於agent的數量的,數據包到達後會在隊列裏等待thread調用agent的邏輯來處理。這是一種比較常見的設計方法,但要注意的是因爲agent都放在一個進程裏,程序的健壯性要求很高,一個進程core則會致使全服玩家掉線。而使用C++編寫也增長了宕進程的可能性……..你懂的。(2)使用Java編寫,對於這種「中心節點」式架構來講多是更好的選擇,起碼不是由於一個玩家的誤操做(可能使用外掛)致使全服玩家掉線。(3)雲風彷佛是使用lua coroutine來實現agent的相互隔離和協同工做的,這樣能夠減小單一agent失敗對其餘agent的影響(動態語言的好處)。
sharedb
sharedb在系統中的地位看上去像是database前端的cache,但就本人的理解sharedb的做用遠不止是一個數據緩存。
和天龍八部的ShareMemory相似,sharedb也採用了定長的結構化數據(見《開發筆記 (6)》),經過共享內存來實現進程間的數據共享。sharedb的存在使得遊戲邏輯處理和數據保存邏輯獲得很好的隔離,遊戲邏輯不用關心後端的數據是如何保存的,只要sharedb掛上按期存盤的服務,在接口定義明確的狀況下,後端到底採用什麼樣的數據庫變得不是那麼重要,從而下降了系統的耦合度。
3、服務器底層框架skynet
skynet的設計思想見《Skynet 設計綜述》:
我但願咱們的遊戲服務器(但 skynet 不只限於用於遊戲服務器)可以充分利用多核優點,將不一樣的業務放在獨立的執行環境中處理,協同工做。這個執行環境,最先的時候,我指望是利用 OS 的進程,後來發現,若是咱們一定採用嵌入式語言,好比 Lua 的話,獨立 OS 進程的意義不太大。Lua State 已經提供了良好的沙盒,隔離不一樣執行環境。而多線程模式,可使得狀態共享、數據交換更加高效。而多線程模型的諸多弊端,好比複雜的線程鎖、線程調度問題等,均可以經過減少底層的規模,精簡設計,最終把危害限制在很小的範圍內。這一點,Skynet 最終花了不到 3000 行 C 代碼來實現核心層的代碼,一個稍有經驗的 C 程序員,均可以在短期理解,作維護工做。
作爲核心功能,Skynet 僅解決一個問題:
把一個符合規範的 C 模塊,從動態庫(so 文件)中啓動起來,綁定一個永不重複(即便模塊退出)的數字 id 作爲其 handle 。模塊被稱爲服務(Service),服務間能夠自由發送消息。每一個模塊能夠向 Skynet 框架註冊一個 callback 函數,用來接收發給它的消息。每一個服務都是被一個個消息包驅動,當沒有包到來的時候,它們就會處於掛起狀態,對 CPU 資源零消耗。若是須要自主邏輯,則能夠利用 Skynet 系統提供的 timeout 消息,按期觸發。
Skynet 提供了名字服務,還能夠給特定的服務起一個易讀的名字,而不是用 id 來指代它。id 和運行時態相關,沒法保證每次啓動服務,都有一致的 id ,但名字能夠。
本人感受skynet像一個發佈訂閱的消息中間件(還沒看源碼,可能有誤),這種基於服務的即插即用式的框架給服務器端帶來很大的可擴展性,同時也使得各模塊之間獨立清晰,具備良好的可維護性。可是這裏有個疑問,服務都以so的形式掛在skynet上,那麼這些服務從哪裏獲取玩家、怪物、NPC等object的數據?是從skynet中得到仍是直接從sharedb中得到,出於性能的考慮是否是要把skynet和sharedb部署在同一臺物理主機上?這樣一來就會增長設計和具體邏輯的耦合度。看了《Skynet 集羣及 RPC》,感受skynet上的服務是要經過skynet來得到玩家的數據,這樣操做會不會致使數據被複制不少次,不知道最終的效率是否受到影響?
4、gate
知足服務器劃分原則裏的第一點:分離遊戲中佔用系統資源(cpu,內存,IO等)較多的功能,獨立成服務器。
gate的主要工做能夠參見本系列blog的第二篇《網絡遊戲服務器構架設計(二):刀劍Online - 鏈接負載服務器CLS》
5、場景管理器
主要用於管理靜態場景和動態副本,好比agent登陸時查詢本身所在場景對應的服務器地址。
6、場景服務器
場景服務器的內容我沒有從《開發筆記》中獲得太多的信息(可能level過低),更多的是以功能模塊的形式寫,好比AOI。不過其中有一點比較新穎的是雲風認爲player的位置、動做狀態,戰鬥數值狀態等都是場景的一部份,應該保存在場景中而不是agent中。本節有所更正見(八)補充。
據個人猜想,場景服務器應該會負責:
須要注意的是場景服務器修改的一些數據應該以什麼樣的頻率通知agent呢?好比player的位置信息,該信息是徹底保存在場景數據裏仍是說agent裏也有一份?
7、總結
本文是一篇雲風《開發筆記》系列blog的讀後感,所述內容均是本人的猜想,雖恐貽笑大方,但也但願能拋磚引玉。收筆忐忑ing!
8、補充:
(1) 雲風在微薄上的回覆是:「咱們最終採用的是單進程多線程, 每線程上一個 lua state 的結構. sharedb 是用來線程間數據交換的. gate 和 sharedb 以及 loader 和 agent map 同樣, 都是 skynet 下的獨立服務, 以 so 掛接進去的. 後來的商品交易, 掉落品分配也是 skynet 下的服務. 」
(2) 關於場景服務器,雲風已經給出完整的說明,見《開發筆記(14)》
場景服務分紅兩個部分,一是副本管理器,二是地圖服務。在角色數據上,記錄有角色應該屬於的地圖。agent 向地圖所屬的副本管理器查詢,獲得他所屬的地圖服務地址。即可以把本身註冊到具體地圖上。
地圖服務管理了全部其中的角色 id ,以及若干 npc 。他的義務在於把讓這些 id 對應的 agent 相互瞭解。但具體邏輯則放在每一個 agent 服務上。每一個 agent 本身所屬進程 attach 其它 id ,能夠獲取其餘對象的狀態。.........
(3) 風哥在《開發筆記(25)》中已經提到最終使用單進程多線程的模式。看來簡單設計是有共識的:-)