您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“經(jīng)典游戲服務(wù)器端架構(gòu)實例分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習“經(jīng)典游戲服務(wù)器端架構(gòu)實例分析”吧!
現(xiàn)代電子游戲,基本上都會使用一定的網(wǎng)絡(luò)功能。從驗證正版,到多人交互等等,都需要架設(shè)一些專用的服務(wù)器,以及編寫在服務(wù)器上的程序。因此,游戲服務(wù)器端軟件的架構(gòu),本質(zhì)上也是游戲服務(wù)器這個特定領(lǐng)域的軟件架構(gòu)。
軟件架構(gòu)的分析,可以通過不同的層面入手。比較經(jīng)典的軟件架構(gòu)描述,包含了以下幾種架構(gòu):
運行時架構(gòu)——這種架構(gòu)關(guān)心如何解決運行效率問題,通常以程序進程圖、數(shù)據(jù)流圖為表達方式。在大多數(shù)開發(fā)團隊的架構(gòu)設(shè)計文檔中,都會包含運行時架構(gòu),說明這是一種非常重要的設(shè)計方面。這種架構(gòu)也會顯著的影響軟件代碼的開發(fā)效率和部署效率。本文主要討論的是這種架構(gòu)。
邏輯架構(gòu)——這種架構(gòu)關(guān)心軟件代碼之間的關(guān)系,主要目的是為了提高軟件應(yīng)對需求變更的便利性。人們往往會以類圖、模塊圖來表達這種架構(gòu)。這種架構(gòu)設(shè)計在需要長期運營和重用性高的項目中,有至關(guān)重要的作用。因為軟件的可擴展性和可重用度基本是由這個方面的設(shè)計決定的。特別是在游戲領(lǐng)域,需求變更的頻繁程度,在多個互聯(lián)網(wǎng)產(chǎn)業(yè)領(lǐng)域里可以說是最高的。本文會涉及一部分這種架構(gòu)的內(nèi)容,但不是本文的討論重點。
物理架構(gòu)——關(guān)心軟件如何部署,以機房、服務(wù)器、網(wǎng)絡(luò)設(shè)備為主要描述對象。
數(shù)據(jù)架構(gòu)——關(guān)心軟件涉及的數(shù)據(jù)結(jié)構(gòu)的設(shè)計,對于數(shù)據(jù)分析挖掘,多系統(tǒng)協(xié)作有較大的意義。
開發(fā)架構(gòu)——關(guān)心軟件開發(fā)庫之間的關(guān)系,以及版本管理、開發(fā)工具、編譯構(gòu)建的設(shè)計,主要為了提高多人協(xié)作開發(fā),以及復(fù)雜軟件庫引用的開發(fā)效率?,F(xiàn)在流行的集成構(gòu)建系統(tǒng)就是一種開發(fā)架構(gòu)的理論。
服務(wù)器端軟件的本質(zhì),是一個會長期運行的程序,并且它還要服務(wù)于多個不定時,不定地點的網(wǎng)絡(luò)請求。所以這類軟件的特點是要非常關(guān)注穩(wěn)定性和性能。這類程序如果需要多個協(xié)作來提高承載能力,則還要關(guān)注部署和擴容的便利性;同時,還需要考慮如何實現(xiàn)某種程度容災(zāi)需求。由于多進程協(xié)同工作,也帶來了開發(fā)的復(fù)雜度,這也是需要關(guān)注的問題。
功能約束,是架構(gòu)設(shè)計決定性因素。一個萬能的架構(gòu),必定是無能的架構(gòu)。一個優(yōu)秀的架構(gòu),則是正好把握了對應(yīng)業(yè)務(wù)領(lǐng)域的核心功能產(chǎn)生的。游戲領(lǐng)域的功能特征,于服務(wù)器端系統(tǒng)來說,非常明顯的表現(xiàn)為幾個功能的需求:
對于游戲數(shù)據(jù)和玩家數(shù)據(jù)的存儲
對玩家客戶端進行數(shù)據(jù)廣播
把一部分游戲邏輯在服務(wù)器上運算,便于游戲更新內(nèi)容,以及防止外掛。
針對以上的需求特征,在服務(wù)器端軟件開發(fā)上,我們往往會關(guān)注軟件對電腦內(nèi)存和CPU的使用,以求在特定業(yè)務(wù)代碼下,能盡量滿足承載量和響應(yīng)延遲的需求。最基本的做法就是“時空轉(zhuǎn)換”,用各種緩存的方式來開發(fā)程序,以求在CPU時間和內(nèi)存空間上取得合適的平衡。在CPU和內(nèi)存之上,是另外一個約束因素:網(wǎng)卡。網(wǎng)絡(luò)帶寬直接限制了服務(wù)器的處理能力,所以游戲服務(wù)器架構(gòu)也必定要考慮這個因素。
對于游戲服務(wù)器架構(gòu)設(shè)計來說,最重要的是利用游戲產(chǎn)品的需求約束,從而優(yōu)化出對此特定功能最合適的“時-空”架構(gòu)。并且最小化對網(wǎng)絡(luò)帶寬的占用。
[圖-游戲服務(wù)器的分析模型]
基于上述的分析模型,對于游戲服務(wù)端架構(gòu),最重要的三個部分就是,如何使用CPU、內(nèi)存、網(wǎng)卡的設(shè)計:
內(nèi)存架構(gòu):主要決定服務(wù)器如何使用內(nèi)存,以保證盡量少的內(nèi)存泄漏的可能,以及最大化利用服務(wù)器端內(nèi)存來提高承載量,降低服務(wù)延遲。
調(diào)度架構(gòu):設(shè)計如何使用進程、線程、協(xié)程這些對于CPU調(diào)度的方案。選擇同步、異步等不同的編程模型,以提高服務(wù)器的穩(wěn)定性和承載量。同時也要考慮對于開發(fā)帶來的復(fù)雜度問題?,F(xiàn)在出現(xiàn)的虛擬化技術(shù),如虛擬機、docker、云服務(wù)器等,都為調(diào)度架構(gòu)提供了更多的選擇。
通信模式:決定使用何種方式通訊。網(wǎng)絡(luò)通訊包含有傳輸層的選擇,如TCP/UDP;據(jù)表達層的選擇,如定義協(xié)議;以及應(yīng)用層的接口設(shè)計,如消息隊列、事件分發(fā)、遠程調(diào)用等。
本文的討論,也主要是集中于對以上三個架構(gòu)的分析。
最早的游戲服務(wù)器是比較簡單的,如UO《網(wǎng)絡(luò)創(chuàng)世紀》的服務(wù)端一張3.5寸軟盤就能存下?;旧现皇且粋€廣播和存儲文件的服務(wù)器程序。后來由于國內(nèi)的外掛、盜版流行,各游戲廠商開始以MUD為模型,建立主要運行邏輯在服務(wù)器端的架構(gòu)。這種架構(gòu)在MMORPG類產(chǎn)品的不斷更新中發(fā)揚光大,從而出現(xiàn)了以地圖、視野等分布要素設(shè)計的分布式游戲服務(wù)器。而在另外一個領(lǐng)域,休閑游戲,天然的需要集中超高的在線用戶,所以全區(qū)型架構(gòu)開始出現(xiàn)?,F(xiàn)代的游戲服務(wù)器架構(gòu),基本上都希望能結(jié)合承載量和擴展性的有點來設(shè)計,從而形成了更加豐富多樣的形態(tài)。
本文的討論主要是選取這些比較典型的游戲服務(wù)器模型,分析其底層各種選擇的優(yōu)點和缺點,希望能探討出更具廣泛性,更高開發(fā)效率的服務(wù)器模型。
分服模型是游戲服務(wù)器中最典型,也是歷久最悠久的模型。其特征是游戲服務(wù)器是一個個單獨的世界。每個服務(wù)器的帳號是獨立的,而且只用同一服務(wù)器的帳號才能產(chǎn)生線上交互。在早期服務(wù)器的承載量達到上限的時候,游戲開發(fā)者就通過架設(shè)更多的服務(wù)器來解決。這樣提供了很多個游戲的“平行世界”,讓游戲中的人人之間的比較,產(chǎn)生了更多的空間。所以后來以服務(wù)器的開放、合并形成了一套成熟的運營手段。一個技術(shù)上的選擇最后導(dǎo)致了游戲運營方式的模式,是一個非常有趣的現(xiàn)象。
[圖-單進程調(diào)度模型]
同步-動態(tài)多線程:每接收一個用戶會話,就建立一個線程。這個用戶會話往往就是由客戶端的TCP連接來代表,這樣每次從socket中調(diào)用讀取或?qū)懗鰯?shù)據(jù)包的時候,都可以使用阻塞模式,編碼直觀而簡單。有多少個游戲客戶端的連接,就有多少個線程。但是這個方案也有很明顯的缺點,就是服務(wù)器容易產(chǎn)生大量的線程,這對于內(nèi)存占用不好控制,同時線程切換也會造成CPU的性能損失。更重要的多線程下對同一塊數(shù)據(jù)的讀寫,需要處理鎖的問題,這可能讓代碼變的非常復(fù)雜,造成各種死鎖的BUG,影響服務(wù)器的穩(wěn)定性。
同步-多線程池:為了節(jié)約線程的建立和釋放,建立了一個線程池。每個用戶會話建立的時候,向線程池申請?zhí)幚砭€程的使用。在用戶會話結(jié)束的時候,線程不退出,而是向線程池“釋放”對此線程的使用。線程池能很好的控制線程數(shù)量,可以防止用戶暴漲下對服務(wù)器造成的連接沖擊,形成一種排隊進入的機制。但是線程池本身的實現(xiàn)比較復(fù)雜,而“申請”、“施放”線程的調(diào)用規(guī)則需要嚴格遵守,否則會出現(xiàn)線程泄露,耗盡線程池。
異步-單線程/協(xié)程:在游戲行業(yè)中,采用Linux的epoll作為網(wǎng)絡(luò)API,以期得到高性能,是一個常見的選擇。游戲服務(wù)器進程中最常見的阻塞調(diào)用就是網(wǎng)路IO,因此在采用epoll之后,整個服務(wù)器進程就可能變得完全沒有阻塞調(diào)用,這樣只需要一個線程即可。這徹底解決了多線程的鎖問題,而且也簡化了對于并發(fā)編程的難度。但是,“所有調(diào)用都不得阻塞”的約束,并不是那么容易遵守的,比如有些數(shù)據(jù)庫的API就是阻塞的;另外單進程單線程只能使用一個CPU,在現(xiàn)在多核多CPU的服務(wù)器情況下,不能充分利用CPU資源。異步編程由于是基于“回調(diào)”的方式,會導(dǎo)致要定義很多回調(diào)函數(shù),并且把一個流程里面的邏輯,分別寫在多個不同的回調(diào)函數(shù)里面,對于代碼閱讀非常不理?!槍@種編碼問題,協(xié)程(Coroutine)能較好的幫忙,所以現(xiàn)在比較流行使用異步+協(xié)程的組合。不管怎樣,異步-單線程模型由于性能好,無需并發(fā)思維,依然是現(xiàn)在很多團隊的首選。
異步-固定多線程:這是基于異步-單線程模型進化出來的一種模型。這種模型一般有三類線程:主線程、IO線程、邏輯線程。這些線程都在內(nèi)部以全異步的方式運行,而他們之間通過無鎖消息隊列通信。
多進程游戲服務(wù)器
多進程的游戲服務(wù)器系統(tǒng),最早起源于對于性能問題需求。由于單進程架構(gòu)下,總會存在承載量的極限,越是復(fù)雜的游戲,其單進程承載量就越低,因此開發(fā)者們一定要突破進程的限制,才能支撐更復(fù)雜的游戲。
一旦走上多進程之路,開發(fā)者們還發(fā)現(xiàn)了多進程系統(tǒng)的其他一些好處:能夠利用上多核CPU能力;利用操作系統(tǒng)的工具能更仔細的監(jiān)控到運行狀態(tài)、更容易進行容災(zāi)處理。多進程系統(tǒng)比較經(jīng)典的模型是“三層架構(gòu)”:
在多進程架構(gòu)下,開發(fā)者一般傾向于把每個模塊的功能,都單獨開發(fā)成一個進程,然后以使用進程間通信來協(xié)調(diào)處理完整的邏輯。這種思想是典型的“管道與過濾器”架構(gòu)模式思想——把每個進程看成是一個過濾器,用戶發(fā)來的數(shù)據(jù)包,流經(jīng)多個過濾器銜接而成的管道,最后被完整的處理完。由于使用了多進程,所以首選使用單進程單線程來構(gòu)造其中的每個進程。這樣對于程序開發(fā)來說,結(jié)構(gòu)清晰簡單很多,也能獲得更高的性能。
[圖-對象樹架構(gòu)]
在Objective C語言中,有所謂autorealse的特性,這種特性實際上是一種引用計數(shù)的技術(shù)。由于能配合在某個調(diào)度模型下,所以使用起來會比較簡單。同樣的思想,有些開發(fā)者會使用一些智能指針,配合自己寫的框架,在完整的業(yè)務(wù)邏輯調(diào)用后一次性清理相關(guān)內(nèi)存。
[圖-預(yù)分配內(nèi)存池]
不過這樣做,同樣有一些缺點:首先是不太好部署,比如你想在某個資源較小的虛擬機上部署一套用來測試,可能一位內(nèi)沒改內(nèi)存池的大小,導(dǎo)致啟動不成功。每次更換環(huán)境都需要修改這個配置。其次,是所有的用到的類對象,都要在根節(jié)點對象那里有個指針或者引用,否則就可能泄漏內(nèi)存。由于對于非基本類型的對象,我們一般不喜歡用拷貝的方式來作為函數(shù)的參數(shù)和返回值,而指針和應(yīng)用所指向的內(nèi)存,如果不能new的話,只能是現(xiàn)成的某個對象的成員屬性。這回導(dǎo)致程序越復(fù)雜,這類的成員屬性就越多,這些屬性在代碼維護是一個不小的負擔。
要解決以上的缺點,可以修改內(nèi)存池的實現(xiàn),為動態(tài)增長,但是具備上限的模型,每次從內(nèi)存池中“獲取”對象的時候才new。這樣就能避免在小內(nèi)存機器上啟動不了的問題。對于對象屬性復(fù)雜的問題,一般上需要好好的按面向?qū)ο蟮脑瓌t規(guī)劃代碼,做到盡量少用僅僅表示函數(shù)參數(shù)和返回值的屬性,而是主要是記錄對象的“業(yè)務(wù)狀態(tài)”屬性為主,多花點功夫在構(gòu)建游戲的數(shù)據(jù)模型上。
在多進程的系統(tǒng)中,進程間如何通訊是一個至關(guān)重要的問題,其性能和使用便利性,直接決定了多進程系統(tǒng)的技術(shù)效能。
Socket通訊
TCP/IP協(xié)議是一種通用的、跨語言、跨操作系統(tǒng)、跨機器的通訊方案。這也是開發(fā)者首先想到的一種手段。在使用上,有使用TCP和UDP兩個選擇。一般我們傾向在游戲系統(tǒng)中使用TCP,因為游戲數(shù)據(jù)的邏輯相關(guān)性比較強,UDP由于可能存在的丟包和重發(fā)處理,在游戲邏輯上的處理一般比較復(fù)雜。由于多進程系統(tǒng)的進程間網(wǎng)絡(luò)一般情況較好,UDP的性能優(yōu)勢不會特別明顯。
要使用TCP做跨進程通訊,首先就是要寫一個TCP Server,做端口監(jiān)聽和連接管理;其次需要對可能用到的通信內(nèi)容做協(xié)議定制;最后是要編寫編解碼和業(yè)務(wù)邏輯轉(zhuǎn)發(fā)的邏輯。這些都完成了之后,才能真正的開始用來作為進程間通信手段。
使用Socket編程的好處是通用性廣,你可以用來實現(xiàn)任何的功能,和任何的進程進行協(xié)作。但是其缺點也異常明顯,就是開發(fā)量很大。雖然現(xiàn)在有一些開源組件,可以幫你簡化Socket Server的編寫工作,簡化連接管理和消息分發(fā)的處理,但是選擇目標建立連接、定制協(xié)議編解碼這兩個工作往往還是要自己去做。游戲的特點是業(yè)務(wù)邏輯變化很多,導(dǎo)致協(xié)議修改的工作量非常大。因此我們除了直接使用TCP/IP socket以外,還有很多其他的方案可以嘗試。
[圖-消息隊列]
需要注意的是,有些開發(fā)者缺乏經(jīng)驗,使用了數(shù)據(jù)庫,如MySQL,或者是NFS這類運行效率比較低的媒介作為隊列的存儲者。這在功能上雖然可以行得通,但是操作一頻繁,就難以發(fā)揮作用了。如以前有一些手機短信應(yīng)用系統(tǒng),就用MySQL來存儲“待發(fā)送”的短信。
消息隊列雖然非常好用,但是我們還是要自己對消息進行編解碼,并且分發(fā)給所需要的處理程序。在消息到處理程序之間,存在著一個轉(zhuǎn)換和對應(yīng)的工作。由于游戲邏輯的繁多,這種對應(yīng)工作完全靠手工編碼,是比較容易出錯的。所以這里還有進一步的改進空間。
遠程調(diào)用
有一些開發(fā)者會希望,在編碼的時候完全屏蔽是否跨進程在進行調(diào)用,完全可以好像調(diào)用本地函數(shù)或者本地對象的方法一樣。于是誕生了很多遠程調(diào)用的方案,最經(jīng)典的有Corba方案,它試圖實現(xiàn)能在不同語言的代碼直接,實現(xiàn)遠程調(diào)用。JAVA虛擬機自帶了RMI方案的支持,在JAVA進程之間遠程調(diào)用是比較方便的。在互聯(lián)網(wǎng)的環(huán)境下,還有各種Web Service方案,以HTTP協(xié)議作為承載,WSDL作為接口描述。
使用遠程調(diào)用的方案,最大好處是開發(fā)的便捷,你只需要寫一個函數(shù),就能在任何一個其他進程上對此函數(shù)進行調(diào)用。這對游戲開發(fā)來說,就解決了多進程方案最大的一個開發(fā)效率問題。但是這種便捷是有成本的:一般來說,遠程調(diào)用的性能會稍微差一點,因為需要用一套統(tǒng)一的編解碼方案。如果你使用的是C/C++這類靜態(tài)語言,還需要使用一種IDL語言來先描述這種遠程函數(shù)的接口。但是這些困難帶來的好處,在游戲開發(fā)領(lǐng)域還是非常值得的。
[圖-服務(wù)器狀態(tài)管理]
盡管用簡單的目錄服務(wù)器可以實現(xiàn)大部分容災(zāi)和擴容的需求,但是如果被訪問進程的內(nèi)存中有數(shù)據(jù)存在,那么問題就比較復(fù)雜了。對于容災(zāi)來說,新的進程必須要有辦法重建那個“失效”了的進程內(nèi)存中的數(shù)據(jù),才可能完成容災(zāi)功能;對于擴容功能來說,新加入的進程,也必須能把需要的數(shù)據(jù)載入到自己的內(nèi)存中才行,而這些數(shù)據(jù),可能已經(jīng)存在于其他平行的進程中,如何把這部分數(shù)據(jù)轉(zhuǎn)移過來,是一個比較耗費性能和需要編寫相當多代碼的工作?!砸话阄覀兿矚g對“無狀態(tài)”的進程來做擴容和容災(zāi)。
到此,相信大家對“經(jīng)典游戲服務(wù)器端架構(gòu)實例分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。