Figure.1
3. 軟件架構(gòu)
HS是被設(shè)計(jì)成通用性模塊,理論上可以供所有應(yīng)用服務(wù)器接入使用,如Figure.2所示:
Figure.2
由于HS模塊自身并沒有網(wǎng)絡(luò)傳輸功能,在集成中還需要將應(yīng)用服務(wù)器的網(wǎng)絡(luò)I/O接口接入到HS模塊(常規(guī)情況下,應(yīng)用服務(wù)器的網(wǎng)絡(luò)I/O模塊都會(huì)提供單獨(dú)接口?)。
整體架構(gòu)如Figure.3,HS自己運(yùn)行一個(gè)線程,對(duì)所有異步傳遞到HS模塊的數(shù)據(jù),先使用queue進(jìn)行排隊(duì)緩存,再在本線程內(nèi)一次性將其傳輸?shù)脚渲玫腟lave端,同時(shí)這部分?jǐn)?shù)據(jù)也會(huì)寫入模塊的第三方存儲(chǔ)(HS目前只實(shí)現(xiàn)了Memory存儲(chǔ))。
Slave在接收到Master傳輸?shù)膁ata后,更新應(yīng)用服務(wù)器相關(guān)內(nèi)存(根據(jù)Master發(fā)送的KEY可以確定具體的內(nèi)存類型),同時(shí)也會(huì)將接收到的數(shù)據(jù)寫入自身的數(shù)據(jù)緩沖隊(duì)列和第三方存儲(chǔ)。
Master和Slave只要編寫很少代碼(發(fā)送時(shí)HOOK,接收時(shí)更新)就可以實(shí)現(xiàn)一個(gè)完備的hot-standby server。
Figure.3
這里就有個(gè)疑問了,為什么Slave也需要維護(hù)一個(gè)queue和第三方存儲(chǔ)呢?這是因?yàn)镾lave可能會(huì)在未來的某一天會(huì)變成Master,需要同步數(shù)據(jù)給未來的一個(gè)Slave。Figure.4流程圖說明了服務(wù)器啟動(dòng)的過程。
Figure.4
4. 模塊化
HS可以單獨(dú)編譯成動(dòng)態(tài)庫形式(.so)使用。
具體實(shí)現(xiàn)中使用boost的一些庫,主要有io_service,thread,bind,shared_ptr等。
不過在編譯時(shí)可以選擇將boost庫靜態(tài)編譯,實(shí)際使用上就不用單獨(dú)進(jìn)行依賴了。
需要說明的是,為了更好的支持HS的運(yùn)行,遠(yuǎn)程過程調(diào)用模塊被開發(fā)出來,這里就不詳述了。
5. 網(wǎng)絡(luò)傳輸如何處理突如其來的大數(shù)據(jù)量
在某種極端情況下,當(dāng)Master啟動(dòng)了很久之后,Slave才姍姍來遲,Master可能積累了大量的數(shù)據(jù)(超過4G),這個(gè)時(shí)候如果Master對(duì)Slave進(jìn)行無限制的傳輸數(shù)據(jù),會(huì)占用大量的網(wǎng)卡資源(不過一般雙網(wǎng)卡,不影響外網(wǎng)服務(wù)),最重要的會(huì)占用大量網(wǎng)絡(luò)線程資源(HS和應(yīng)用服務(wù)器公用網(wǎng)絡(luò)線程)。
實(shí)際測(cè)試中發(fā)現(xiàn),由于4G的小塊數(shù)據(jù)大小不一,傳輸耗時(shí)超過1分鐘。為了避免對(duì)應(yīng)用服務(wù)器的網(wǎng)絡(luò)線程擁塞,加入了傳輸數(shù)據(jù)的流量控制,在每個(gè)間隔時(shí)間內(nèi)只傳輸指定配置的數(shù)據(jù)量,測(cè)試證明,這個(gè)方法解決了所有相關(guān)問題。
第三方存儲(chǔ)有什么用?
Slave端斷開后或者在Master啟動(dòng)一段時(shí)間后再連上來時(shí),用于存儲(chǔ)歷史備份數(shù)據(jù)。就是說Slave 在斷開很長(zhǎng)時(shí)間之后再連上來,期間所有的歷史數(shù)據(jù)都是在第三方存儲(chǔ)中。連上后,首先需要同步的第三方存儲(chǔ)的數(shù)據(jù),完成后再對(duì)queue中的數(shù)據(jù)進(jìn)行實(shí)時(shí)同步。
6. Socket的幽靈屬性keepalive
為了檢測(cè)網(wǎng)線斷開或者不可知的網(wǎng)絡(luò)異常,HS需要一套能夠檢測(cè)網(wǎng)絡(luò)真斷開,還是偽斷開(比如網(wǎng)絡(luò)短時(shí)間內(nèi)的不通),首先想到了利用TCP/IP協(xié)議自身的屬性,即KEEPALIVE,經(jīng)使用測(cè)試發(fā)現(xiàn),這個(gè)不是很好的檢查機(jī)制,至少不是我們需要的。因?yàn)镵EEPALIVE依賴系統(tǒng)的三個(gè)屬性,如下所示:
系統(tǒng)設(shè)置
# cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
# cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
簡(jiǎn)單說明下,這個(gè)三個(gè)屬性表示TCP/IP協(xié)議層會(huì)在7200秒沒有收到客戶端消息的情況下,發(fā)送10(9+1)個(gè)報(bào)文段,每?jī)蓚€(gè)間隔75秒,若客戶端對(duì)這些信息都沒有響應(yīng),則終止該連接。
不使用這個(gè)屬性的兩個(gè)理由,一是這些信息屬于系統(tǒng)屬性級(jí)別的改動(dòng),二是實(shí)際測(cè)試中發(fā)現(xiàn)很不穩(wěn)定。
以下為設(shè)置keepalive的代碼:
int optval = 1;
socketlen_t optlen = sizeof(optval);
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
更有效的心跳方案:
就是自己實(shí)現(xiàn),方案比較土,簡(jiǎn)單描述下:
服務(wù)器維護(hù)兩個(gè)變量A和B,一個(gè)定時(shí)器,客戶端每來一個(gè)請(qǐng)求包,A變量自增1,同時(shí)在定時(shí)器到點(diǎn)時(shí),檢測(cè)A和B是否相同,如果不同表明客戶端是活動(dòng)的,同時(shí)將A同步到B,開始下一輪定時(shí)器。若一個(gè)連接有N次的定時(shí)器到點(diǎn)是都未活動(dòng),則判定客戶端斷開,關(guān)閉該連接。
7. OVER
【編者加注】
專注于社交游戲的研發(fā)與運(yùn)營,逐漸對(duì)社交游戲產(chǎn)品的業(yè)務(wù)越來越熟悉,不斷地總結(jié)與分析,加快社交游戲產(chǎn)品的研發(fā)速度,我們技術(shù)團(tuán)隊(duì)做很多研究與嘗試,為此我們開發(fā)出來產(chǎn)品:
1) 數(shù)據(jù)中間件:解決大用戶并發(fā)與大數(shù)據(jù)量的問題,不需要游戲研發(fā)工程師關(guān)心數(shù)據(jù)的存取等;
2) 任務(wù)服務(wù)器:解決游戲產(chǎn)品眾多活動(dòng)或獎(jiǎng)勵(lì)活動(dòng)的舉辦,以及游戲自身任務(wù)的配置與管理;
3) 統(tǒng)計(jì)服務(wù)器:我們玩家的操作日志數(shù)據(jù)量太大,無法全部存儲(chǔ)到服務(wù)器上,為此有選擇地存儲(chǔ)相關(guān)玩家的行為日志數(shù)據(jù),并且完成數(shù)據(jù)分析的工作;
4) 好友列表服務(wù)器:社交游戲產(chǎn)品主要是接入眾多的社交網(wǎng)站平臺(tái),為此要實(shí)現(xiàn)一套通用的好友列表服務(wù)器;
5) …..等其他游戲引擎組建
隨著推出上述相關(guān)通用性游戲引擎的組建,我們也逐漸贏得更多時(shí)間與精力可以做更有意義的事情,比如我們可以靜下心研究解決這些服務(wù)器作為單點(diǎn)運(yùn)行的問題,為此研發(fā)出解決各個(gè)游戲引擎組建服務(wù)的主備提供服務(wù)的通用性組建,也即其他游戲引擎組建通過引入hot-standby組建,可以解決單點(diǎn)故障的問題。這個(gè)通用性組建,也可以為非社交游戲行業(yè)的服務(wù)器后臺(tái)程序的熱備模式,提供一定的技術(shù)參考價(jià)值。感謝@ZEROV17的投稿支持,也歡迎各位技術(shù)朋友站內(nèi)留言或者新浪微博直接交流,同時(shí)也非常感謝零度視角F為此項(xiàng)目所作的貢獻(xiàn)!