最近調(diào)研了一下游戲服務(wù)器設(shè)計(jì)的問題粱腻,周末花了些時(shí)間查閱相關(guān)的書籍和文章再沧,也了解了一下Photon尼夺、KBEngine、Skynet這些開源游戲服務(wù)器的結(jié)構(gòu)和實(shí)現(xiàn)炒瘸,邊學(xué)習(xí)淤堵,邊整理。
Web服務(wù)器設(shè)計(jì)
在思考游戲服務(wù)器設(shè)計(jì)的時(shí)候顷扩,我首先想到的是一般Web服務(wù)器的設(shè)計(jì)結(jié)構(gòu)拐邪。最簡單而典型的Web服務(wù)器模型可以總結(jié)為:
服務(wù)端創(chuàng)建一個(gè)監(jiān)聽Socket持續(xù)等待新連接,客戶端發(fā)起TCP連接隘截,然后三次握手成功建立連接扎阶。接著客戶端(瀏覽器)發(fā)送HTTP請求給服務(wù)器汹胃,服務(wù)器返回HTTP響應(yīng)回復(fù),并在客戶端呈現(xiàn)用戶想要的內(nèi)容东臀。
在這個(gè)基礎(chǔ)上着饥,再進(jìn)一步,如果是運(yùn)行有Web App應(yīng)用的服務(wù)器惰赋,則會(huì)加入Web框架宰掉,來進(jìn)行多個(gè)應(yīng)用模塊的管理、使用及調(diào)度赁濒,例如python中的Django轨奄、Flask都是優(yōu)雅而易用的框架。如果在一個(gè)服務(wù)器上需要運(yùn)行多個(gè)框架及應(yīng)用的時(shí)候拒炎,會(huì)考慮在服務(wù)器和框架之間加入中間件組件挪拟,它可以根據(jù)目標(biāo)URL將請求消息路由到不同的框架應(yīng)用,也可以進(jìn)行負(fù)載均衡击你,例如針對python的WSGI接口設(shè)計(jì)舞丛。在這種情況下,Web服務(wù)器模型可以總結(jié)為:
服務(wù)端監(jiān)聽客戶端連接果漾,客戶端發(fā)起連接球切,創(chuàng)建連接后,客戶端發(fā)送HTTP請求給服務(wù)器绒障。中間件通過消息路由吨凑、轉(zhuǎn)發(fā)請求找到框架,提供可調(diào)用的應(yīng)用户辱。服務(wù)器調(diào)用可調(diào)用的應(yīng)用鸵钝,處理接收到的HTTP請求÷洌框架/應(yīng)用產(chǎn)生HTTP狀態(tài)和HTTP響應(yīng)頭和響應(yīng)體傳遞給服務(wù)器恩商。由服務(wù)器組裝成一個(gè)完整的報(bào)文返回給客戶端,并在客戶端呈現(xiàn)用戶想要的內(nèi)容必逆。
在此基礎(chǔ)上更進(jìn)一步怠堪,大型的Web網(wǎng)站和動(dòng)態(tài)應(yīng)用,我理解的服務(wù)器設(shè)計(jì)就是指后臺整體設(shè)計(jì)了名眉,包括:
- 基礎(chǔ)服務(wù)器配置(Apache粟矿、Nginx)
- Web應(yīng)用框架和中間件
- 數(shù)據(jù)庫IO(MySQL/Oracle/MongoDB、優(yōu)化存儲(chǔ)损拢、讀寫分離陌粹、負(fù)載均衡、數(shù)據(jù)與應(yīng)用分離等)
- 緩存系統(tǒng)(常分為文件緩存福压、內(nèi)存緩存和數(shù)據(jù)庫緩存掏秩,在大型應(yīng)用中使用最多且效率最高的是內(nèi)存緩存或舞,現(xiàn)在常用的內(nèi)存緩存工具有Memcached和Redis。而且現(xiàn)在大家都比較關(guān)注圖片緩存優(yōu)化,常常有專門針對圖片的服務(wù)器設(shè)計(jì))
- 分布式存儲(chǔ)系統(tǒng)(Hadoop蒙幻、Spark映凳、HBase對這方面還不熟)
-
分布式服務(wù)器管理系統(tǒng)(批量化的任務(wù)執(zhí)行、配置文件杆煞、命令管理魏宽、腳本程序腐泻、補(bǔ)丁安裝等等决乎,運(yùn)維的工作)
代碼發(fā)布系統(tǒng)(生產(chǎn)環(huán)境下以虛擬主機(jī)方式提供,源代碼管理和版本控制派桩,集群間代碼同步构诚,參與內(nèi)部開發(fā)、內(nèi)部測試铆惑、生產(chǎn)環(huán)境測試范嘱、生產(chǎn)環(huán)境發(fā)布4個(gè)開發(fā)階段的管理)
游戲服務(wù)器設(shè)計(jì)
Web服務(wù)器的設(shè)計(jì)大概可以這樣去劃分。那么游戲服務(wù)器和Web服務(wù)器又有什么樣的不同呢员魏?
首先丑蛤,根據(jù)前面的描述,一般的Web Application撕阎,是典型的Request-Response模式受裹,通過TCP和服務(wù)器建立短連接,而請求數(shù)據(jù)和影響數(shù)據(jù)通過HTTP協(xié)議進(jìn)行組裝虏束,當(dāng)完成交互的時(shí)候棉饶,服務(wù)器端和客戶端TCP連接就會(huì)釋放,把服務(wù)器端socket資源留給新的客戶端镇匀。游戲服務(wù)器最大的特點(diǎn)是多數(shù)要求通信的實(shí)時(shí)性照藻,客戶端和服務(wù)器端通常采用長連接(并非絕對),客戶端會(huì)主動(dòng)給服務(wù)器發(fā)送數(shù)據(jù)汗侵,服務(wù)器也可能主動(dòng)往客戶端發(fā)送數(shù)據(jù)幸缕,生命周期比較長,一次發(fā)送的數(shù)據(jù)量比較小晰韵,但是數(shù)據(jù)交互發(fā)送比較頻繁冀值。由于要進(jìn)行長連接,服務(wù)器端的socket就不能進(jìn)行復(fù)用宫屠。其次列疗,在web程序中,客戶端之間的數(shù)據(jù)是沒有交互的浪蹂,所有的數(shù)據(jù)都是通過web服務(wù)器響應(yīng)給客戶端抵栈;但是網(wǎng)游服務(wù)器中告材,每個(gè)客戶端的數(shù)據(jù)的變化,都要通過服務(wù)器端廣播給其他客戶端進(jìn)行同步古劲。所以客戶端會(huì)有上限斥赋,同時(shí)服務(wù)器要進(jìn)行分區(qū),一個(gè)區(qū)里面同時(shí)在線人數(shù)會(huì)有限制产艾。
除了這兩方面不同外疤剑,其實(shí)很多Web服務(wù)器的設(shè)計(jì)方法是可以應(yīng)用到游戲服務(wù)器上的,下面是我設(shè)想的一種游戲服務(wù)器設(shè)計(jì):
關(guān)于這個(gè)設(shè)計(jì)的說明:
- 結(jié)構(gòu)
- 網(wǎng)關(guān)服務(wù)器:負(fù)責(zé)監(jiān)聽和維持客戶端連接闷堡,數(shù)據(jù)發(fā)送到游戲邏輯服務(wù)器隘膘,客戶端和游戲服務(wù)器之間的消息轉(zhuǎn)發(fā)和加密解密,沒有具體游戲邏輯杠览,可能需要設(shè)定連接數(shù)量弯菊。
- 游戲服務(wù)器:游戲進(jìn)程實(shí)現(xiàn),負(fù)責(zé)提供游戲邏輯踱阿,與數(shù)據(jù)庫服務(wù)器通信負(fù)責(zé)數(shù)據(jù)交互管钳。一般單進(jìn)程或單線程模型可以應(yīng)付,有的采用其他解決方案(比如Photon的纖程)软舌。這里采用多個(gè)游戲服務(wù)器來模擬多個(gè)區(qū)服情況才漆。
- 數(shù)據(jù)庫服務(wù)器:專門用來處理與數(shù)據(jù)庫的連接、查詢佛点、數(shù)據(jù)存盤等操作醇滥。方便游戲服務(wù)器異步讀寫數(shù)據(jù)庫的數(shù)據(jù)。
- 管理服務(wù)器:有點(diǎn)像混合P2P通信里的中心節(jié)點(diǎn)恋脚,負(fù)責(zé)管理所有的游戲邏輯服務(wù)器以及他們之間的之間消息轉(zhuǎn)發(fā)腺办,提供廣播到所有游戲服務(wù)器的功能。
- 戰(zhàn)斗服務(wù)器:考慮到多人MMO同步操作的實(shí)時(shí)性要求糟描,是否需要單獨(dú)設(shè)計(jì)這么一個(gè)部分不是很確定怀喉。
-
數(shù)據(jù)庫
目前一般是在關(guān)系型數(shù)據(jù)庫mysql和非關(guān)系型數(shù)據(jù)庫mongodb兩者中選型。數(shù)據(jù)庫寫操作采用周期存盤方式船响,每隔固定時(shí)間存盤一次躬拢,比如10秒或者15秒,減小數(shù)據(jù)庫壓力见间。采用Memcached或者Redis分布式內(nèi)存緩存方案來減少讀數(shù)據(jù)庫的壓力聊闯。 -
通信協(xié)議
客戶端與服務(wù)器之間協(xié)議通信,弱聯(lián)網(wǎng)手游米诉,一般采用http協(xié)議通信即可菱蔬。大型MMORPG網(wǎng)游大多數(shù)都采用TCP協(xié)議或者HTTP和TCP結(jié)合,客戶端和游戲服采用http協(xié)議。多人戰(zhàn)斗情況下與戰(zhàn)斗服通信采用tcp長連接拴泌。用UDP的情況主要是為了提高通信效率魏身,需要自己做丟包重傳和組裝、校驗(yàn)的工作蚪腐。服務(wù)器之間通信可以采用目前較為流行的ZeroMQ消息隊(duì)列箭昵,然后也有很多優(yōu)秀的開源通信庫可以用來實(shí)現(xiàn)通信協(xié)議。 -
開發(fā)語言
業(yè)界主要的是c/c++ + python/lua模式做游戲服務(wù)器回季。c/c++做網(wǎng)絡(luò)通訊數(shù)據(jù)傳輸家制,python/lua做業(yè)務(wù)邏輯。這樣既保持了網(wǎng)絡(luò)傳輸?shù)男?c++)泡一,又提升開發(fā)效率(python/lua)颤殴,同時(shí)也支持熱更新。 -
其他
-
protobuf:google提供的序列化和反序列化組件瘾杭,將自定義類型的數(shù)據(jù)轉(zhuǎn)化為二進(jìn)制序列傳輸诅病。優(yōu)勢是對于傳輸比較大的數(shù)據(jù)產(chǎn)生的數(shù)據(jù)很緊湊很小哪亿,可以明顯減小傳輸量粥烁。而且處理速度也比較快,又有各種編程語言的實(shí)現(xiàn)蝇棉,例如C++,Java,PHP等等讨阻。缺點(diǎn)是不能明文編輯(數(shù)據(jù)是二進(jìn)制的)。
用protobuf rpc進(jìn)行數(shù)據(jù)傳輸很方便篡殷,但需要自己實(shí)現(xiàn)钝吮。 - ZeroMQ:穩(wěn)健,簡潔的多進(jìn)程消息隊(duì)列實(shí)現(xiàn)方案板辽。ZeroMQ 不一定基于 TCP 協(xié)議奇瘦,它也可以用于進(jìn)程間和進(jìn)程內(nèi)通訊。在這里它更適合服務(wù)器與服務(wù)器之間的通信劲弦,邏輯服和戰(zhàn)斗服之間的通信耳标。
-
memcached:高性能的分布式內(nèi)存對象緩存系統(tǒng),用于動(dòng)態(tài)Web應(yīng)用以減輕數(shù)據(jù)庫負(fù)載邑跪。它通過在內(nèi)存中緩存數(shù)據(jù)和對象來減少讀取數(shù)據(jù)庫的次數(shù)次坡,不需要每次操作都訪問數(shù)據(jù)庫,提升性能画畅≡依牛基于http協(xié)議的通信可以用memcached,如果是tcp長鏈接轴踱,就維護(hù)一個(gè)在線的內(nèi)存對象症脂。類似的技術(shù)還可以考慮redis等。
log4net/glog/zlog:日志記錄組件。定義好日志級別:error / debug / fatal / info诱篷,做好時(shí)間戳沸版。
-
protobuf:google提供的序列化和反序列化組件瘾杭,將自定義類型的數(shù)據(jù)轉(zhuǎn)化為二進(jìn)制序列傳輸诅病。優(yōu)勢是對于傳輸比較大的數(shù)據(jù)產(chǎn)生的數(shù)據(jù)很緊湊很小哪亿,可以明顯減小傳輸量粥烁。而且處理速度也比較快,又有各種編程語言的實(shí)現(xiàn)蝇棉,例如C++,Java,PHP等等讨阻。缺點(diǎn)是不能明文編輯(數(shù)據(jù)是二進(jìn)制的)。
一些服務(wù)器框架##
最后,附上一些目前正在進(jìn)行兴蒸,而且應(yīng)該今后較長一段時(shí)間都會(huì)在看的一些優(yōu)雅而易用的服務(wù)器框架吧:
Photon:目前在Unity 3D手游方面用的最廣泛的服務(wù)器框架视粮,支持部署自己的服務(wù)器,Photon也有提供云服務(wù)器橙凳。運(yùn)行于Windows平臺蕾殴,底層C++實(shí)現(xiàn),上層采用C#作為開發(fā)語言岛啸。更多的關(guān)于Photon的內(nèi)容在這里钓觉。
KBEngine(github):同樣應(yīng)用于Unity 3D的網(wǎng)游開源服務(wù)器框架,C++ + Python的模式坚踩,文檔和視頻教程還比較全荡灾。有一個(gè)不錯(cuò)的Demo可以學(xué)習(xí)。
Firefly(github)9秒社區(qū)開發(fā)的一款基于python的游戲服務(wù)器框架瞬铸,剛出來不久的小鮮肉批幌。
SkyNet: 國內(nèi)的游戲開發(fā)大神云風(fēng)寫的一套基于Actor并發(fā)模型的服務(wù)端底層管理框架,看了一下嗓节,不得要領(lǐng)T_T荧缘。
Tornado:基于python的Web開發(fā)框架,被Facebook拦宣、知乎和豆瓣采用截粗。特點(diǎn)是實(shí)現(xiàn)異步非阻塞的I/O模型。因?yàn)檫@個(gè)模型和Unity 3D中的協(xié)程很像鸵隧,所以挺有興趣绸罗。
可能隨著以后對于服務(wù)器開發(fā)的深入實(shí)踐再回頭看這篇文章會(huì)發(fā)現(xiàn)很多幼稚和錯(cuò)誤吧,不過偶爾思考和實(shí)現(xiàn)這些像模型一樣的設(shè)計(jì)的東西也挺有趣的豆瘫。