http://www.infoq.com/cn/articles/game-server-development-1
游戲服務(wù)器概述沒開發(fā)過游戲的人會覺得游戲服務(wù)器是很神秘的東西。但事實(shí)上它并不比web服務(wù)器復(fù)雜,無非是給客戶端提供網(wǎng)絡(luò)請求服務(wù),本質(zhì)上它只是基于長連接的socket服務(wù)器。當(dāng)然在邏輯復(fù)雜性、消息量、實(shí)時性方面有更高的要求。
游戲服務(wù)器是復(fù)雜的socket服務(wù)器。
如果說web服務(wù)器的本質(zhì)是http服務(wù)器,那么游戲服務(wù)器的本質(zhì)就是socket服務(wù)器。 它利用socket通訊來實(shí)現(xiàn)服務(wù)器與客戶端之間的交互。事實(shí)上有不少游戲是直接基于原生socket來開發(fā)的。 相對于簡單的socket服務(wù)器,它承受著更加煩重的任務(wù):
- 后端承載著極復(fù)雜的游戲邏輯。
- 網(wǎng)絡(luò)流量與消息量巨大,且實(shí)時性要求極高。
- 通常一臺socket服務(wù)器無法支撐復(fù)雜的游戲邏輯,因此在socket服務(wù)器的背后還有一個服務(wù)器群。
為什么純粹的socket服務(wù)器還不夠好?
很多web應(yīng)用不會基于原生的http服務(wù)器搭建,一般都會基于某類應(yīng)用服務(wù)器(如tomcat)搭建,而且還會利用一些開發(fā)框架來簡化web開發(fā)。 同樣,一般游戲服務(wù)器的開發(fā)都會在socket服務(wù)器上封裝出一套框架或類似的應(yīng)用服務(wù)器。為什么使用原生的socket接口開發(fā)不夠好呢?
相關(guān)贊助商
QCon北京2016大會,4月21-23日,北京·國際會議中心,精彩內(nèi)容邀您參與!
- 抽象程度。原生的socket抽象程度過低,接口過于底層,很多機(jī)制都需要自己封裝,如Session、filter、請求抽象、廣播等機(jī)制都要自已實(shí)現(xiàn),工作量很大,容易出錯,且有很多的重復(fù)勞動。
- 可伸縮性。高可伸縮性需考慮很多問題,消息密度、存儲策略、進(jìn)程架構(gòu)等因素都需要考慮。用原生的socket要達(dá)到高可伸縮性,需要在架構(gòu)上花費(fèi)大量的功夫,而且效果也未必能達(dá)到開源框架的水準(zhǔn)。
- 服務(wù)端的監(jiān)控、管理。很多服務(wù)器的數(shù)據(jù)需要監(jiān)控,例如消息密度、在線人數(shù)、機(jī)器壓力、網(wǎng)絡(luò)壓力等,如果采用原生socket,所有這些都要自己開發(fā),代價很大。
用框架來簡化游戲服務(wù)器開發(fā)
一個好的框架可以大大簡化游戲服務(wù)器的工作。除了游戲自身的邏輯外,大部分的工作都可以用框架來解決。服務(wù)端的抽象,可伸縮性,可擴(kuò)展性這些問題都可以通過框架來解決。 游戲服務(wù)器框架也承擔(dān)了應(yīng)用服務(wù)器的功能。可以把框架看成容器,只要把符合容器標(biāo)準(zhǔn)的代碼扔進(jìn)去,容器就運(yùn)行起來了。它自然具備了抽象能力、可伸縮性和監(jiān)控、管理等能力。
游戲服務(wù)器框架介紹
在開源社區(qū)里充斥了數(shù)不清的web服務(wù)器框架,游戲客戶端的框架和庫也有一大堆,但唯獨(dú)游戲服務(wù)器框架少之又少,零星有一些類庫,但完整的解決方案幾乎沒有。我們只好從商用的解決方案中拿出一些框架進(jìn)行類比:
RedDwarf是唯一一個能找到的完整的開源游戲服務(wù)器框架,由sun出品??上г谒喜⒌絆racle以后已經(jīng)停止開發(fā)了。 在設(shè)計上,RedDwarf是個分布式架構(gòu),它在分布式數(shù)據(jù)存儲和任務(wù)管理上投入了太多精力,而且做的過于理想化,如動態(tài)任務(wù)遷移功能的實(shí)現(xiàn)非常復(fù)雜,但實(shí)際應(yīng)用中根本用不到。而在可伸縮性和性能的設(shè)計上不太理想。因此RedDwarf夭折了。
SmartfoxServer是由意大利的一家游戲公司gotoAndPlay()推出的商用游戲服務(wù)器。 它是基于java開發(fā)的,與web應(yīng)用服務(wù)器如Tomcat看上去很類似。Smartfox支持各種客戶端,且有一些成功案例。它在服務(wù)端封裝和監(jiān)控管理方面實(shí)現(xiàn)得很完善。 但在可伸縮性上并不是太理想,盡管Smartfox也支持Cluster模式,但它的擴(kuò)展方式是基于jvm內(nèi)存復(fù)制的。也沒有實(shí)現(xiàn)傳統(tǒng)MMORPG基于場景分區(qū)的解決方案。 Smartfox有免費(fèi)版本,但完全不開源。而且它的免費(fèi)版本(達(dá)不到高并發(fā)用戶要求)很大程度是為了吸引開發(fā)者最終購買它的收費(fèi)版本。不限在線人數(shù)的收費(fèi)版本價格達(dá)到3500美刀。
Bigworld是澳大利亞Bigworld公司開發(fā)的全套3d MMORPG游戲解決方案,解決方案包含了客戶端和服務(wù)端。Bigworld功能非常強(qiáng)大,在動態(tài)負(fù)載均衡和容錯性做了很多工作。可擴(kuò)展性非常強(qiáng)大。 它的缺點(diǎn)是過于重量級,對硬件要求高,且價格非常昂貴。Bigworld是專門為3d MMORPG游戲定制,但并不適用于中小型游戲的開發(fā)。
Pomelo是網(wǎng)易于2012年11月推出的開源游戲服務(wù)器。它是基于node.js開發(fā)的高性能、可伸縮、輕量級游戲服務(wù)器框架。 它的主要優(yōu)勢有以下幾點(diǎn):
- 開發(fā)模型快速、易上手,基于Convention over configuration的原則,讓代碼達(dá)到最大的簡化。
- 架構(gòu)的可伸縮性和可擴(kuò)展性好,pomelo在服務(wù)器擴(kuò)展和應(yīng)用擴(kuò)展上實(shí)現(xiàn)得非常方便。
- 輕量級,雖然是分布式架構(gòu),但啟動非常迅速,占用資源少。
- 參考全面,框架不僅提供了完整的中英文檔,還提供了完整的MMO demo代碼(客戶端html5),可以作為很好的開發(fā)參考。
Pomelo目前的主要缺點(diǎn)是推出時間尚短,一些功能還在完善中,支持的客戶端類型還有限,目前已支持HTML5、ios、android、untiy3d等4類客戶端,未來還會支持更多的客戶端類型。
游戲服務(wù)器的可伸縮性探討
不管是web應(yīng)用還是游戲服務(wù)器,可伸縮性始終是最重要的指標(biāo),也是最棘手的問題,它涉及到系統(tǒng)運(yùn)行架構(gòu)的搭建,各種優(yōu)化策略。 只有把可伸縮性設(shè)計好了,游戲的規(guī)模、同時在線人數(shù)、響應(yīng)時間等參數(shù)才能得到保證。
為什么游戲服務(wù)器的可伸縮性遠(yuǎn)遠(yuǎn)不及web?
相比web應(yīng)用幾乎無限擴(kuò)展的架構(gòu)(前提是架構(gòu)設(shè)計得好),游戲服務(wù)器的可伸縮性相比就著差遠(yuǎn)了。那么是哪些因素導(dǎo)致游戲無法達(dá)到web應(yīng)用的擴(kuò)展能力呢? 說明:本文提到的web應(yīng)用不包括類似于聊天這樣的高實(shí)時web應(yīng)用,高實(shí)時web可認(rèn)為是一種邏輯較簡單的游戲。
長連接和響應(yīng)實(shí)時性
web應(yīng)用都是基于request/response的短連接模式。占用的資源要比一直hold長連接的游戲服務(wù)器要少很多。Web應(yīng)用能使用短連接模式的原因如下:
- 通訊的單向性,普通web應(yīng)用一般只有拉模式
- 響應(yīng)的實(shí)時性要求不高,一般web應(yīng)用的響應(yīng)時間在3秒以內(nèi)都算響應(yīng)比較及時的。
而游戲應(yīng)用只能使用長連接,原因如下:
- 通訊的雙向性,游戲應(yīng)用不僅僅是推拉模式,而且推送的數(shù)據(jù)量要遠(yuǎn)遠(yuǎn)大于拉的數(shù)據(jù)量
- 響應(yīng)的實(shí)時性要求極高,一般游戲應(yīng)用要求推送的消息實(shí)時反映,而實(shí)時響應(yīng)的最大時間是100ms。
在高并發(fā)長連接服務(wù)的解決方案中,目前除了傳統(tǒng)的C語言(過于重量級)實(shí)現(xiàn),用的最多的是erlang與node.js。兩者的性能指標(biāo)差不多,而node.js在易用性方面毫無疑問勝出太多。
最近微博上看到時go的能撐起100萬的并發(fā)連接,node.js也能達(dá)到同樣的數(shù)據(jù), Node.js w/1M concurrent connections!有node.js的長連接數(shù)據(jù),它占用了16G內(nèi)存,但CPU還遠(yuǎn)沒跑滿。
交互的相鄰性與分區(qū)策略
普通的web應(yīng)用在交互上沒有相鄰性的概念,所有用戶之間的交互都是平等,交互頻率也不受地域限制。 而游戲則不然,游戲交互跟玩家所在地圖(場景)上的位置關(guān)系非常大,如兩個玩家在相鄰的地方可以互相PK或組隊打怪。這種相鄰的交互頻率非常高,對實(shí)時性的要求也非常高,這就必須要求相鄰?fù)婕以诜植荚谕粋€進(jìn)程里。 于是就有了按場景分區(qū)的策略,如圖所示:

一個進(jìn)程里可以有一個場景,也可以有多個場景。這種實(shí)現(xiàn)帶來了以下問題:
- 游戲的可伸縮性受到場景進(jìn)程的限制,如果某個場景過于煩忙可能會把進(jìn)程撐爆,也就把整個游戲撐爆。
- 場景服務(wù)器是有狀態(tài)的,每個用戶請求必須發(fā)回原來的場景服務(wù)器。服務(wù)器的有狀態(tài)帶來一系列的問題:場景進(jìn)程的可伸縮,高可用性等都比不上web服務(wù)器。目前只能通過游戲服務(wù)器的隔離來緩解這些問題。
廣播
游戲中廣播的代價是非常大的。玩家的輸入與輸出是不對等的,玩家自己簡單地動一下,就需要將這個消息實(shí)時推送給所有看到這個玩家的其他玩家。 假如場景里面人較少,廣播發(fā)送的消息數(shù)還不多,但如果人數(shù)達(dá)到很密集的程度,則廣播的頻度將呈平方級增長。如圖所示:

假如場景中1000個玩家,每人發(fā)1條消息,如果需要其它玩家都看到的話,消息的推送量將高達(dá)1,000,000條,這足以把任何服務(wù)器撐爆。
解決這個問題的方案:
- 減少消息數(shù)量---消息只發(fā)送給能看到的玩家。玩家能看到的只是屏幕的大小,而不是整張地圖的大小,這樣推送消息的時候可以只推給對自己的狀態(tài)感興趣的玩家。這個可以用AOI(area of interested)算法來實(shí)現(xiàn),在pomelo的庫pomelo-aoi中實(shí)現(xiàn)了簡單的燈塔算法。
- 分擔(dān)負(fù)載,將消息推送的進(jìn)程與具體的邏輯進(jìn)程分離。如圖:

這樣廣播邏輯與具體的進(jìn)程邏輯就不會相互影響了,而且由于只有后端的場景服務(wù)器是有狀態(tài)的,前端負(fù)責(zé)廣播的服務(wù)器還是無狀態(tài)的,因此前端服務(wù)器可以無限擴(kuò)展。
實(shí)時Tick
實(shí)時游戲的服務(wù)端一般都需要一個定時tick來執(zhí)行定時任務(wù),為了游戲的實(shí)時性,一般要求這個tick時間在100ms之內(nèi)。這些任務(wù)包括以下邏輯:
- 遍歷場景中的實(shí)體(包括玩家、怪物等),進(jìn)行定時操作,如移動、復(fù)活、消失等邏輯。
- 定期補(bǔ)充場景中被殺掉的怪的數(shù)量。
- 定期執(zhí)行AI操作,如怪物的攻擊、逃跑等邏輯。
由于實(shí)時100ms的限制,這個實(shí)時tick的執(zhí)行時間必須要遠(yuǎn)少于100ms,因此單進(jìn)程內(nèi)很多數(shù)據(jù)都會受到限制。
- 場景內(nèi)實(shí)體的數(shù)量受限制,因?yàn)橐闅v所有實(shí)體
- 注意更新的算法,所有的算法,包括AI在內(nèi)都要在幾十毫秒全部完成
- 注意GC,full GC最好永遠(yuǎn)不要發(fā)生。一般full GC的時間都會高于100ms,幸好node.js在內(nèi)存少于500M時表現(xiàn)良好,只有小GC。因此一定要控制內(nèi)存大小。
- 盡量分進(jìn)程,進(jìn)程的粒度越少,出現(xiàn)tick超時或full GC的可能越少。在多核時代里,CPU是最廉價的資源。
高可伸縮的運(yùn)行架構(gòu)
經(jīng)過以上這些分析。我們可以得到現(xiàn)在的運(yùn)行架構(gòu),如下圖:

運(yùn)行架構(gòu)說明:
- 客戶端通過websocket長連接連到connector服務(wù)器群。
- connector負(fù)責(zé)承載連接,并把請求轉(zhuǎn)發(fā)到后端的服務(wù)器群。
- 后端的服務(wù)器群主要包括按場景分區(qū)的場景服務(wù)器(area)、聊天服務(wù)器(chat)和狀態(tài)服務(wù)器等(status),這些服務(wù)器負(fù)責(zé)各自的業(yè)務(wù)邏輯。真實(shí)的案例中還會有各種其它類型的服務(wù)器。
- 后端服務(wù)器處理完邏輯后把結(jié)果返回給connector,再由connector廣播回給客戶端。 master負(fù)責(zé)統(tǒng)一管理這些服務(wù)器,包括各服務(wù)器的啟動、監(jiān)控和關(guān)閉等功能。
這個運(yùn)行架構(gòu)符合了剛才提到的幾個伸縮性原則:
- 前后端進(jìn)程分離,把承載連接和廣播的壓力盡量分出去。
- 進(jìn)程的粒度盡量小,把功能細(xì)分到各個服務(wù)器
- 按場景分區(qū)
前面提到4個游戲服務(wù)器框架,只有bigworld和pomelo符合這樣的架構(gòu),當(dāng)然bigworld實(shí)現(xiàn)的還要更復(fù)雜。 現(xiàn)在的問題是,這個運(yùn)行架構(gòu)是個分布式架構(gòu),而且并不簡單,那就帶來以下問題:
- 需要多少的代碼來實(shí)現(xiàn)這樣的運(yùn)行架構(gòu)?
- 服務(wù)器類型、數(shù)量管理和擴(kuò)展有點(diǎn)復(fù)雜,該怎么管理?
- 服務(wù)器之間會有一堆的相互rpc調(diào)用,實(shí)現(xiàn)起來怎么簡化?
- 分布式的開發(fā)和調(diào)試并不容易,消耗資源量過大,過于重量級,多進(jìn)程bug定位困難,該怎么解決? Pomelo和node.js將很輕松地幫我們解決這些難題,我們下一節(jié)將討論。
node.js、pomelo與游戲服務(wù)器
Node.js的特點(diǎn)與游戲服務(wù)器極其符合。列舉如下:
- 對網(wǎng)絡(luò)IO的處理能力,node.js生來就是為IO而生的,而游戲服務(wù)器剛好是網(wǎng)絡(luò)密集型的應(yīng)用。
- 單線程的應(yīng)用模型,node.js的單線程處理能力遠(yuǎn)比其它語言強(qiáng)大,而單線程處理游戲邏輯是最簡單,最不容易出錯,而且不可能出現(xiàn)死鎖、鎖競爭的情況。
- 語言與輕量的開發(fā)模型。Javascript語言已經(jīng)不是昔日的吳下阿蒙,它不僅由于腳本語言的輕量、簡單帶來了開發(fā)效率的提升。還可以與一些類型的客戶端共享部分代碼,如html5,unity3d的js客戶端等。
- 語言的動態(tài)性帶來了很多框架設(shè)計的便利,如設(shè)計DSL,實(shí)現(xiàn)Convention over configuration。盡管這方面比ruby稍差,但在pomelo框架中使用已經(jīng)足夠好了。
Pomelo是基于node.js搭建的游戲服務(wù)器框架,它在靈活性、擴(kuò)展能力,輕量級調(diào)試方面具有無可比擬的優(yōu)勢。我們先簡單回答第三章最末的幾個問題:
- 用pomelo來實(shí)現(xiàn)以上的運(yùn)行架構(gòu)幾乎是零代碼的,因?yàn)樗谠O(shè)計時天生就具備這樣的架構(gòu)。
- 服務(wù)器類型、數(shù)量的管理極簡單,利用鴨子類型、目錄定義,只要一個簡單json配置文件就可以實(shí)現(xiàn)所有服務(wù)器的管理。
- rpc調(diào)用可以實(shí)現(xiàn)完全零配置,也不用生成stub。感謝js語言的動態(tài)性,基于Convention over configuration的原則,可以直接實(shí)現(xiàn)rpc調(diào)用。
- 分布式的開發(fā)和調(diào)試只占用很少的資源,啟動極其迅速,十幾個進(jìn)程只用10秒不到的時間完全啟動。多進(jìn)程調(diào)試與單進(jìn)程調(diào)試沒有任何區(qū)別,只在一個console里搞定。
在本系列文章后面將會陸續(xù)討論pomelo是怎么實(shí)現(xiàn)以上如此方便的特性, 以及這些設(shè)計帶來的啟發(fā)。
小結(jié)
本文分析了游戲服務(wù)器框架的市場現(xiàn)狀,一個高可伸縮游戲服務(wù)器架構(gòu)的設(shè)計原則及運(yùn)行架構(gòu)。Node.js與pomelo在解決高并發(fā)和分布式架構(gòu)中起到的作用。下文我們將深入分析pomelo在解決復(fù)雜的游戲服務(wù)器運(yùn)行架構(gòu)中提供了哪些便利。
posted on 2016-03-14 09:12
思月行云 閱讀(436)
評論(0) 編輯 收藏 引用 所屬分類:
Node.js