Node.js之網遊伺服器實踐

網易雲社群發表於2018-10-30

此文已由作者堯飄海授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。


隨著Node.js的不斷髮展與壯大,應用範圍也越來越廣泛,從傳統的企業應用,到網際網路使用,再到雲端計算的發展,它的身影也是隨處可見。當然,它的受歡迎程度能在短時間內得到這麼快的發展,除卻與其本身的事件模型及V8的效能優化等一系列特性有關之外,還和國內外很多網際網路公司的攻城師的大量應用和參與到開源專案中有密切關係,如網易的遊戲開發,淘寶的資料之美等等。隨著HTML5應用和移動網際網路平臺的指數增長,越來越多的使用者使用了移動平臺的休閒服務,採用 Node.js 實現高效能和可擴充套件性的遊戲服務將是一件有意義的工作。

在網際網路上,目前有一些採用 Node.js 實現的開源遊戲服務框架,如 Mozilla的 Browser Quest , Google的 Grits ,Chilly等。但是無一例外,這些框架不但與遊戲邏輯聯絡緊密,而且幾乎沒有可擴充套件性和效能資料,同時也不提供任何遊戲開發的管理工具,除了採用 JavaScript 編寫外,很難體現出採用 Node.js 實現遊戲開發的優越性。

概念

通常遊戲分為角色扮演類和策略類及混合類等幾種遊戲型別。那麼在網頁遊戲型別中,根據遊戲的型別,開發者可能採用不同的架構實現方式,如策略類遊戲可能更偏重於遊戲的策略性和邏輯性,也就是考驗遊戲玩家的各種組合或搭配之類的遊戲,對實時性的要求不會很高,在使用者的可接受範圍之內即可,因此也可以採用常用的Web應用開發模式來實現,而客戶端採用輪詢或長連線等方式來實現,這些應用模式也有很多的相關經驗可以參考,因此能做到具有較好的可擴充套件性及伸縮性;此外,應用伺服器和服務框架等也可以採用現存的技術實現,但是這隻能滿足對遊戲實時性要求不高的場景服務,由於此類的應用方案非常成熟,在下文的討論中,將不再詳述。

角色扮演類遊戲根據策劃的要求可以實現各種各樣的功能,如聊天,打鬥,自動刷怪等各種複雜的功能,但是開發者基本可以把這些功能抽象地劃分為二大類,即服務端的網路傳輸與邏輯運算能力,這樣遊戲服務的開發和實現會稍微簡單些。現在,網路的實時傳輸基本上是採用Socket伺服器來實現的,邏輯運算這個就不需要再述了,因此,遊戲伺服器就可以粗略的歸納為Socket的伺服器綜合遊戲業務邏輯的實現。

與Web應用區別

從上面遊戲服務的概念簡單介紹中可以得知,實時遊戲伺服器的開發與傳統的Web開發還是具有一些不同點,這些不同點著重體現在以下幾個方面:
大部分的Web應用還是短連線的方式去實現,而遊戲的實時性需要採用長連線的方式進行。

Web應用的場景一般是讀多寫少的應用,熱點資料基本可以快取;遊戲服務的資料經常發生變動,如移動中的路徑,打怪掉血等,快取基本很快就失效,屬於寫多讀少的應用場景。

廣播特性,即Web應用行為上基本只需要使用者與伺服器互動,其他使用者要實現共享他人資訊的時候,會採用類似拉的方式如長輪詢來實現資料互動,即使在沒有資料更新的狀態時也需要消耗一定的服務端資源,造成一定的浪費;遊戲裡面的互動方式需要實時進行,對影響三方的資料,必須採用廣播的方法進行訊息推送,即推的模式。

目前的Web應用開發一般採用無狀態性的方式來實現,因此可以實現較好的可擴充套件性,很多Web服務會採用繫結會話或集中會話等方式工作,當後面的某臺伺服器出現當機時,服務也可以很快的切換;但是遊戲伺服器和使用者之間的連線是有狀態的,在斷開時需要進行相關的通知和資料持久化,在重連時需要進行狀態恢復等手段來維護遊戲世界的執行。

除以上幾點不同之外,還有負載均衡的策略,服務驅動方式,系統判定,系統安全如外掛作弊等相關問題,文章中不再對他們進行對比說明。一般來講,根據遊戲的規模與需求,通常遊戲伺服器的實現的模式會使用如下的三種架構方式來實現。

單程式服務架構

Node.js之網遊伺服器實踐

圖1 單程式服務架構

圖1是目前網際網路上的很多遊戲伺服器使用的架構,比如商用的SmartFoxServer遊戲伺服器,開源的有Google的 Grits, Mozilla的 Browser Quest等框架。所有的程式在同一個程式裡面執行,架構簡單,開發人員上手快,開發和除錯非常方便又快速,成本較低,後期的維護成本可能隨著遊戲專案的大小而不同,但是整體應該不會太高。很明顯,遊戲的世界是在同一個程式裡執行,在單個場景都有不少線上使用者的時候,如果採用單程式的 Node.js 實現的話,每個使用者的操作可能會有一定的延遲,特別是在大量使用者同時做大量操作與AI運算時,如服務端尋路和自動殺怪,延遲會更加嚴重;同時單程式對計算機資源使用有限。因此只適合遊戲的原型製作或小型的遊戲開發。另外,由於遊戲的狀態與複雜性,也無法較好的實現遊戲的可擴充套件性,基本只能通過新增遊戲伺服器的方式來實現,即所謂的開新服,而這些使用者之間是無法在同一個世界裡通訊會話和互動的。儘管Node.js支援原生的TCP服務,使用簡單;但是目前大部分網路應用中,尤其是支援HTML5的應用,socket.io由於效能較好並且使用簡單,成為大部分應用採用的網路庫來實現高效能websocket服務。在移動網際網路應用中,由於協議較複雜與通訊資料等問題,需要一定的裁減。下面示例中,就是最簡單的單程式服務的原型:

var socketio =
require('socket.io');
var io =
socketio.listen(8080);
io.sockets.on('connection',function(socket){
     socket.on('disconnect',function(){
     //user leave
     });
 socket.on('message',function(msg){
     if (getLoginStatus(socket)) {
    game[data.action].apply(null,[socket,msg]); 
     } else {
     game['login'].apply(null,[socket,msg]); 
     }
 });
});複製程式碼


多程式服務架構

Node.js之網遊伺服器實踐

圖2 多程式服務架構

在上圖2中,我們可以看出,每個程式負責採用單一職責,各程式間通過一定的規則(如RPC遠端過程呼叫)來進行通訊,遊戲中的各場景服務使用一個程式來服務,實現各場景遊戲執行的分離。但是在開發過程中,不需要考慮每個場景具體的資訊,全部採用統一的程式碼來實現,開發方便,通過新增適當的引數來實現較易的除錯,每個程式分別執行在伺服器的多核上,即可以充分使用計算機的資源,也由於職責單一,由於每個程式都專職做自己的事,因此,在壓力大的時候,可以很明顯的查出問題所在,並可以較好的實現對相應的服務進行調優,可以達到較好的效能和一定的可伸縮性。但是,在各遊戲的世界裡,無法準確的知道遊戲的狀態如線上人數之類的,會存在熱點資料和服務過載等一些問題,造成部分服務堵死等現象,也無法達到在不停服務的情況下進行伺服器擴容與自動切換等問題,同時會給遊戲運營帶來一定的問題,不能充分的對玩家資料進行分析與挖掘,因此,這種實現方式可以滿足中小型的遊戲開發。這種架構目前很多的遊戲伺服器中還在使用,技術發展也較成熟,如傳奇服務端架構等。

分散式服務架構

Node.js之網遊伺服器實踐

圖3 分散式的服務架構

同樣,分散式的服務架構與多程式的架構具有很多相同點,如程式服務單一職責,較好的效能調優,同時採用集中式的方式對遊戲伺服器進行管理,各遊戲伺服器可以通過自定義的方式即DSL進行遊戲業務的路由與分發,通過主備伺服器的狀態可以很清楚的知道每個服務的狀態及執行情況,這樣可通過動態新增服務來到達負載均衡,具備較好的效能和可擴充套件性,能滿足大型遊戲的開發。當然,採用這樣方式,會具有一定的複雜性,同時各服務之間可能是不在同一臺伺服器上的,因此會帶來一定的網路開銷,在大量廣播的場景中,網路IO壓力大,需要通過定時批量或AOI服務等方式來進行廣播通訊,同時,遊戲的通訊協議需要經過特別的設計,儘量避免資料序列化上的CPU重複開銷,採用胖客戶端的方式去分擔遊戲伺服器的序列化與反序列化;另外還有可能引入分散式事務等相關問題,需要在一個集中點進行處理。當然,在遊戲設計中,策劃開發人員需要儘量避免這種跨場景跨程式的遊戲邏輯需求。

在分散式架構實現中,目前商業上較成功的案例是BigWorld遊戲服務框架,國內外很多大型的遊戲開發商都在使用。而在開源部分,目前還沒有較穩定成熟的方案,我們團隊採用Node.js正在開發的Pomelo遊戲服務框架正在努力實現這一目標,現已進入最後的文件整理階段,預計將在十月份進行開源與線上示例演示。

總結

<span style="display: inline !important; float: none; background-color: transparent; color: rgb(51,

相關文章:
【推薦】 Apache流框架Flink,SparkStreaming,Storm對比分析(一)


相關文章