徐龍光 何頂新
摘 要:消息推送是當前移動應(yīng)用中十分必要的一項技術(shù),服務(wù)者需要使用消息推送以保持用戶活躍度,提高應(yīng)用存留率。為了滿足消息推送的需求和增強推送系統(tǒng)的性能,采用Netty網(wǎng)絡(luò)編程框架并搭建消息推送服務(wù)器集群,使用TCP鏈接發(fā)送心跳包,以保持和維護連接狀態(tài)進行消息推送。通過性能測試,結(jié)果表明服務(wù)器集群可以分散鏈接壓力,有效提高服務(wù)性能。
關(guān)鍵詞:消息推送;Netty;長連接;服務(wù)器集群
DOI:10.11907/rjdk.172207
中圖分類號:TP319
文獻標識碼:A 文章編號:1672-7800(2018)004-0118-02
Abstract:Information pushing system is a necessary technology in current mobile applications. The server needs to use information pushing to keep the user's liveness and increase the retention rate of the application. In order to meet the needs of information pushing and enhance the performance of pushing system, Using Netty network programming framework building a information pushing service cluster, and using TCP toit is proposed to employ Neety network programming framework to build an information pushing service cluster and use TCP to send heartbeat package to send heartbeat package to maintain the connection status. Through the performance test, the results show that the service cluster can distribute link pressure and improve the service performance effectively.
Key Words:information pushing; Netty; long polling; server cluster
0 引言
消息推送是當前移動應(yīng)用和網(wǎng)頁應(yīng)用中常用的一種服務(wù)和技術(shù),目的在于從服務(wù)器端主動向客戶發(fā)送消息。常用的推送技術(shù)包括客戶端輪詢、長連接、APNS、C2DM[1]等,本文采用TCP/IP長連接方式。消息推送有時面臨百萬級甚至更多鏈接數(shù),需要由服務(wù)器集群提供足夠的硬件性能和鏈接穩(wěn)定性。
1 Netty簡介
Netty[2]是一個致力于快速開發(fā)的事件驅(qū)動的異步網(wǎng)絡(luò)編程框架和工具,它基于JAVA NIO開發(fā),開發(fā)者可采用Netty基于Channel對網(wǎng)絡(luò)通信進行開發(fā)。Netty在提供很好性能的同時,極大地簡化了網(wǎng)絡(luò)編程,可進行如TCP、UDP套接字服務(wù)器的開發(fā)。由于Netty的易用性和高性能,其被眾多大型項目采用為網(wǎng)絡(luò)通信模塊的編寫框架。
1.1 Netty優(yōu)化選項
Netty封裝了JAVA NIO的ByteBuffer為ByteBuf,簡化了對緩沖區(qū)的操作。在高性能場景中,可以直接分配堆外內(nèi)存池作為緩沖區(qū),從而減少內(nèi)存拷貝和上下文切換,以此提升性能。Netty下有兩種NIO實現(xiàn)方式,一種基于Select,另一種基于Epoll[3]。相較于Select,Epoll針對有大量鏈接時的情況進行了改進,采用EpollEventLoopGroup替代NioEventLoopGroup作為線程組,在鏈接數(shù)增多時可以顯著提升性能。
2 系統(tǒng)設(shè)計
2.1 協(xié)議設(shè)計
當前有一些基于XMPP協(xié)議的推送服務(wù)方案[4],并且有一些方案是代碼開源的,但是對于單純的推送功能,XMPP協(xié)議有相當程度的冗余?;谙⑼扑偷哪康?,重新設(shè)計一個通信協(xié)議,并且定義消息模型是十分必要的。如圖1所示為傳輸協(xié)議的設(shè)計。2個字節(jié)的消息類型標志位用來表示消息類型,還有2個字節(jié)代表消息體長度,之后是消息體。
消息體設(shè)計代碼如下:
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private String type;
private String content;
private String sender;
private String receiver;
private String format;
private TimeStamp timestamp;
……
}
type 為自定義的消息類型,sender表示消息發(fā)送者賬號,reciver表示消息接受者,content表示消息內(nèi)容,與fortmat結(jié)合可以組成任何消息類型,如text、json、xml等。timestamp為時間戳,用于心跳機制的斷線重連和傳遞消息傳輸時間。
2.2 心跳機制設(shè)計
通常情況下,TCP[5]鏈接一旦連接上,就一直保持開啟狀態(tài),但服務(wù)器和客戶端只是保存了鏈接開啟狀態(tài),并沒有實際的物理通路。并且由于網(wǎng)絡(luò)情況十分復(fù)雜,如果不是正常關(guān)閉TCP鏈接,而是由于網(wǎng)絡(luò)或程序異常導(dǎo)致一方TCP鏈接關(guān)閉,另一方則無法得知鏈接失效,使客戶端不能自動重連,服務(wù)器端也無法得知對方已不在線,不能釋放無效鏈接。雖然現(xiàn)在大多數(shù)TCP協(xié)議的實現(xiàn)中都有保活定時器機制保障鏈接存活,但是其默認超時時間2h過長。不同系統(tǒng)更改超時時間方法不一,沒有通用的API接口,并且在一些特殊的網(wǎng)絡(luò)環(huán)境中還會使?;顧C制失效,所以在應(yīng)用層中實現(xiàn)心跳包是十分必要的。Netty中可以通過將IdleStateHandler類添加進Channel的PipeLine中,實現(xiàn)指定時間內(nèi)沒有讀或?qū)憰r向?qū)Ψ桨l(fā)送心跳包。系統(tǒng)中,設(shè)計鏈接失效時間為100s,每30s如果客戶端沒有寫消息,則向服務(wù)器發(fā)送心跳包;若服務(wù)器沒有回應(yīng),則利用上次服務(wù)器回應(yīng)的消息體中的Timestamp與當前系統(tǒng)時間進行比較;若超過100s則鏈接失效重連。服務(wù)器接到心跳包則給予回應(yīng)。
2.3 服務(wù)器集群設(shè)計
服務(wù)器集群的搭建[6]主要包括一個鏈接調(diào)度服務(wù)器,若干數(shù)據(jù)邏輯服和數(shù)據(jù)庫服務(wù)器,其中,鏈接調(diào)度服務(wù)器也可以使用數(shù)據(jù)邏輯服務(wù)器充當。數(shù)據(jù)邏輯服務(wù)器是客戶端真正連接的服務(wù)器,處理各種業(yè)務(wù)邏輯與推送消息,數(shù)據(jù)庫服務(wù)器基于MysQL[7]構(gòu)建,在本系統(tǒng)中主要負責(zé)存放各個鏈接的信息,使各個獨立的數(shù)據(jù)邏輯服務(wù)器和鏈接調(diào)度服務(wù)器能夠安全地共享數(shù)據(jù)。服務(wù)器集群框架如圖2所示。
不同于只使用單臺服務(wù)器時,客戶端直接向固定服務(wù)器IP發(fā)起登錄請求、建立鏈接,在集群環(huán)境下,客戶端先向鏈接調(diào)度服務(wù)器發(fā)起HTTP請求,獲取相對空閑機器的IP,然后再向該空閑機器發(fā)起登錄請求,建立鏈接。本文自定義一個Session數(shù)據(jù)模型,主要字段包括綁定的Netty Channel名稱,鏈接綁定的服務(wù)器IP、綁定賬戶的ID等,用來管理和操作鏈接。用戶在登錄時將該Session信息存入數(shù)據(jù)庫,可以統(tǒng)計各服務(wù)器的鏈接數(shù),以及尋找到Session存在于哪臺服務(wù)器上。鏈接調(diào)度服務(wù)器采用最少鏈接法[8]對長連接進行調(diào)度,該服務(wù)器利用用戶登錄時存儲在數(shù)據(jù)庫里的鏈接信息,統(tǒng)計各個數(shù)據(jù)邏輯服務(wù)器的鏈接數(shù)量,使新鏈接總是連上最少鏈接數(shù)的數(shù)據(jù)邏輯服務(wù)器。在一臺數(shù)據(jù)邏輯服務(wù)器推送消息時,先判斷當前Session的鏈接是否存在于當前服務(wù)器,若存在,則直接利用該鏈接進行消息推送;若不存在,則需要查找數(shù)據(jù)庫,找到該鏈接存在的服務(wù)器,并向該服務(wù)器轉(zhuǎn)發(fā)消息,并由其進行消息推送。
3 性能測試
通常的性能測試需結(jié)合具體應(yīng)用場景,在生產(chǎn)環(huán)境相同的硬件配置下,估計用戶的實際操作行為,采用一定策略進行模擬,以產(chǎn)生對服務(wù)器的大量請求。而為了與單機時的并發(fā)數(shù)作對比,以測試服務(wù)器集群分散請求的能力,考慮平臺搭建的簡易型,可以使用VMware虛擬來搭建服務(wù)集群進行測試。測試使用的宿主機配置為CPU: Intel E3-1231 v3 3.4GHz,內(nèi)存32GB,硬盤為1T機械硬盤,操作系統(tǒng)為Windows 7。每臺邏輯服務(wù)器設(shè)定虛擬機的CPU數(shù)為4,4GB內(nèi)存,20GB硬盤。測試采用的策略是,采用Apache JMeter設(shè)定TCP并發(fā)鏈接數(shù),并無限循環(huán)給服務(wù)器發(fā)送test字符串,服務(wù)器回應(yīng)received字符串。單臺服務(wù)器測試數(shù)據(jù)如表1所示,兩臺服務(wù)器測試數(shù)據(jù)如表2所示。通過測試可以看出,通過將鏈接壓力分散到不同服務(wù)器上,可以提高并發(fā)鏈接數(shù)。
4 結(jié)語
針對移動端APP中消息推送的需求,本文提出一種基于Netty網(wǎng)絡(luò)編程框架的服務(wù)器集群設(shè)計,介紹了Netty框架的原理及優(yōu)勢,并針對業(yè)務(wù)需求提出了一些優(yōu)化手段,闡述了設(shè)計中的協(xié)議設(shè)計、消息模型、心跳機制以及服務(wù)器集群架構(gòu)。通過性能測試,證明可以通過服務(wù)器集群分散鏈接壓力,提高并發(fā)數(shù),具有一定實用性。
參考文獻:
[1] 張長學(xué),張偉,董智明.移動推送技術(shù)面面觀[J].移動通信,2011(5):21-27.
[2] 李林鋒.Netty權(quán)威指南[M].北京:電子工業(yè)出版社,2014.
[3] 余光遠.基于Epoll的消息推送系統(tǒng)的設(shè)計與實現(xiàn)[D].武漢:華中科技大學(xué),2011.
[4] 代超.基于Netty的面向移動終端的推送服務(wù)設(shè)計[J].軟件,2015,36(12):1-4.
[5] 羅亞非.基于TCP 的Socket 多線程通信[J]. 電腦知識與技術(shù):學(xué)術(shù)交流,2009,5(3):563-565,598.
[6] 胡曉燕.基于服務(wù)器集群的推送技術(shù)的研究與應(yīng)用[D].南京:南京理工大學(xué),2014.
[7] 劉鑫.MySQL 和PostgreSQL 的對比選擇[J]. 沈陽工程學(xué)院學(xué)報,2011,7(2):171-173,177.
[8] 任亨.基于MQTT協(xié)議的消息推送集群系統(tǒng)的設(shè)計與實現(xiàn)[D].沈陽:中國科學(xué)院沈陽計算技術(shù)研究所,2014.
(責(zé)任編輯:黃 ?。?/p>