金志國(guó),李煒
(1 北京郵電大學(xué)網(wǎng)絡(luò)與交換技術(shù)國(guó)家重點(diǎn)實(shí)驗(yàn)室,北京 100876;2 東信北郵信息技術(shù)有限公司,北京 100191)
基于Netty的HTTP客戶端的設(shè)計(jì)與實(shí)現(xiàn)
金志國(guó)1,2,李煒1,2
(1 北京郵電大學(xué)網(wǎng)絡(luò)與交換技術(shù)國(guó)家重點(diǎn)實(shí)驗(yàn)室,北京 100876;2 東信北郵信息技術(shù)有限公司,北京 100191)
能力開放平臺(tái)是一個(gè)提供電信能力開放的移動(dòng)互聯(lián)網(wǎng)開放平臺(tái),向開發(fā)者提供豐富的業(yè)務(wù)能力,包括:短信、彩信、地圖、定位等。能力開放平臺(tái)接入系統(tǒng)需要異步處理客戶端的HTTP請(qǐng)求,而平臺(tái)內(nèi)部各個(gè)系統(tǒng)間的消息傳遞也需要使用大量的異步HTTP請(qǐng)求。Netty是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用開源框架,用于快速開發(fā)可維護(hù)的高性能、高擴(kuò)展性協(xié)議服務(wù)器和客戶端。本文簡(jiǎn)述了通過(guò)對(duì)開源框架Netty的源碼封裝,提供方便易用可以發(fā)送異步HTTP請(qǐng)求的客戶端的設(shè)計(jì)思路和具體實(shí)現(xiàn)。
Netty框架;NIO 技術(shù);異步;HTTP客戶端
云計(jì)算是服務(wù)的交付和使用模式,指通過(guò)網(wǎng)絡(luò)以按需、易擴(kuò)展的方式獲得所需服務(wù)。在云計(jì)算技術(shù)發(fā)展日新月異的今天,業(yè)界開始更多的轉(zhuǎn)向云平臺(tái)。能力開放平臺(tái)是基于PaaS(Platform as a Service,平臺(tái)即服務(wù))提供電信能力開放的移動(dòng)互聯(lián)網(wǎng)開放云平臺(tái),向開發(fā)者提供豐富的業(yè)務(wù)能力,包括:短信、彩信、地圖、定位等。在能力開放平臺(tái)的接入系統(tǒng)部分,需要處理海量的請(qǐng)求。在用戶體驗(yàn)不斷提高的要求下,能力開放平臺(tái)業(yè)務(wù)處理日益復(fù)雜造成交互延遲,傳統(tǒng)的Web請(qǐng)求同步模式下業(yè)務(wù)處理的阻塞造成的延遲已經(jīng)滿足不了高并發(fā)需求[1]。
在接入系統(tǒng)內(nèi)部以及接入系統(tǒng)與其他系統(tǒng)之間,多采用HTTP進(jìn)行數(shù)據(jù)傳輸。由接入系統(tǒng)接受客戶傳來(lái)的請(qǐng)求,進(jìn)行異步處理,但系統(tǒng)內(nèi)部的信息傳輸仍然采用同步模式時(shí),則會(huì)造成阻塞,異步的Web服務(wù)器也失去了意義。所以,在云平臺(tái)項(xiàng)目開發(fā)過(guò)程中急需要一個(gè)可以支持大并發(fā)高效穩(wěn)定的異步HTTP客戶端[2]。
目前,異步HTTP客戶端只有Apache Http Components項(xiàng)目的HttpAsyncClient 4.0-beta4,但仍然是對(duì)外測(cè)試版。其他的異步框架,如Mina、Netty和Grizzly,均是以Java NIO(Non-Blocking I,非阻塞IO)為基礎(chǔ)。本文通過(guò)調(diào)研,選定采用Netty作為底層源碼實(shí)現(xiàn)異步HTTP客戶端。
Netty[3]是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架和工具,用于快速開發(fā)可維護(hù)的高性能、高擴(kuò)展性協(xié)議服務(wù)器和客戶端。Netty是一個(gè)基于Java NIO[4]客戶端/服務(wù)器框架,支持快速、簡(jiǎn)單地開發(fā)網(wǎng)絡(luò)應(yīng)用,如協(xié)議服務(wù)器和客戶端。
圖1 HTTP客戶端總體模塊圖
Netty具有以下優(yōu)勢(shì):
(1)對(duì)阻塞和非阻塞的socket提高統(tǒng)一的API;
(2)高度可定制化的線程模型;
(3)基于攔截鏈模式的事件模型有很高的靈活性和擴(kuò)展性;
(4)對(duì)多種協(xié)議的支持,包括HTTP、TCP、UDP、SMTP、FTP以及二進(jìn)制文本協(xié)議,完全支持SSL/TLS,適用于快速開發(fā)各種高級(jí)組件。
2.1 總體設(shè)計(jì)
本文是以Netty框架[3]為底層,將Netty提供的對(duì)HTTP協(xié)議的支持,編解碼等功能封裝成異步HTTP客戶端,提供簡(jiǎn)單易懂的API使用,可以很容易的發(fā)送HTTP請(qǐng)求,并異步處理HTTP響應(yīng)。
基于Netty的HTTP客戶端總體模塊設(shè)計(jì)[5]見圖1所示,設(shè)計(jì)思路如下:
(1) 提供一個(gè)整體的配置類,作為API的入口;
(2) 針對(duì)不同的配置類,生成不同的異步客戶端啟動(dòng)類;
(3) 由異步客戶端負(fù)責(zé)發(fā)送異步和同步的HTTP請(qǐng)求。
2.2 網(wǎng)絡(luò)模型
Netty基于Java NIO設(shè)計(jì)并實(shí)現(xiàn),因此它實(shí)現(xiàn)異步的方式與Java NIO類似,采用的網(wǎng)絡(luò)模式是reactor模式。
每個(gè)Worker其實(shí)表示的是Selector和Thread一種組合,如圖2所示。每次創(chuàng)建一個(gè)連接,都會(huì)選擇一個(gè)Worker,并且注冊(cè)到Worker里的Selector,Worker會(huì)進(jìn)行l(wèi)oop,不斷地關(guān)注通道事件,也就是會(huì)說(shuō)每個(gè)Worker(Selector)下會(huì)注冊(cè)多個(gè)Channel,并且不斷地去Select關(guān)注的Channel。Selector在發(fā)現(xiàn)感興趣的事件后,直接調(diào)用Channel的Pipeline進(jìn)行處理。
圖2 worker線程模型[3]
2.3 事件驅(qū)動(dòng)模式
消息處理模型采用的是基于事件的Pipeline模式。Pipeline實(shí)際上可以理解為對(duì)Channel的filter chain,可以通過(guò)在Pipeline中定義多個(gè)handler來(lái)處理各種事件。
Channel表示一個(gè)與socket關(guān)聯(lián)的通道。ChannelPipeline是管道,一個(gè)Channel擁有一個(gè)ChannelPipeline,負(fù)責(zé)維護(hù)兩個(gè)處理鏈,即upstream鏈和downstream鏈。處理鏈由多個(gè)處理句柄ChannelHandler構(gòu)成,每個(gè)ChannelHandler處理完以后會(huì)傳遞給鏈中的下一個(gè)處理句柄繼續(xù)處理。ChannelHandler是處理句柄,用戶可以定義自己的處理句柄來(lái)處理每個(gè)請(qǐng)求,或發(fā)出請(qǐng)求前進(jìn)行預(yù)處理,典型的處理句柄有編解碼器decoder和encoder。ChannelEvent事件是整個(gè)模型的處理對(duì)象,當(dāng)產(chǎn)生或觸發(fā)一個(gè)事件時(shí),該事件會(huì)沿著ChannelPipeline處理鏈依次被處理。ChannelFuture表示異步結(jié)果,這個(gè)是異步事件處理的關(guān)鍵,當(dāng)一個(gè)事件被處理時(shí),可以直接以ChannelFuture的形式直接返回,不用在當(dāng)前操作中被阻塞??梢酝ㄟ^(guò)ChannelFuture得到最終的執(zhí)行結(jié)果,具體的做法是在ChannelFuture添加監(jiān)聽器listener,當(dāng)操作最終被執(zhí)行完后,listener會(huì)被觸發(fā),我們可以在listener的回調(diào)函數(shù)中預(yù)定義我們的業(yè)務(wù)代碼。
ChannelPipeline維持兩個(gè)處理鏈:upstream、downstream。Upstream一般處理來(lái)自Channel的讀事件,而downstream一般處理向Channel的寫事件。需要注意的是,這兩個(gè)處理鏈?zhǔn)窍嗷オ?dú)立的,在upstream鏈中傳遞到最后一個(gè)ChannelHandler處理后,不會(huì)再傳遞到downstream鏈中繼續(xù)處理。
在downstream鏈的末端會(huì)有個(gè)ChannelSink處理,用戶可以自定義這個(gè)ChannelSink的實(shí)現(xiàn),系統(tǒng)也有個(gè)默認(rèn)的實(shí)現(xiàn),當(dāng)downstream鏈中最后一個(gè)ChannelHandler處理完后會(huì)被傳遞給這個(gè)ChannelSink進(jìn)行最后的處理。
2.4 詳細(xì)設(shè)計(jì)
2.4.1 配置類
圖3定義了總配置的接口,具體的配置實(shí)現(xiàn)類通過(guò)實(shí)現(xiàn)總配置的接口,實(shí)現(xiàn)自己的配置信息。
圖3 配置類圖
Netty的配置類部分內(nèi)容見表1。
2.4.2 啟動(dòng)類
通過(guò)將配置類NettyConfig作為參數(shù)傳入給異步客戶端類[5], 異步客戶端類會(huì)根據(jù)配置類的類型,返回不同的啟動(dòng)類。如果傳入NettyConfig,則調(diào)用nettyProvider。對(duì)應(yīng)調(diào)用Netty底層的bootstrap幫助類,封裝成異步客戶端啟動(dòng)類返回給用戶。用戶則可以通過(guò)該啟動(dòng)類發(fā)送同步或者異步HTTP請(qǐng)求。
由Netty的網(wǎng)絡(luò)模型(2.2章節(jié))可知,Netty底層采用線程池處理消息的發(fā)送和接受,boss線程和worker分別各用一個(gè)線程池,即需要兩個(gè)線程池。如果每次發(fā)送一條HTTP請(qǐng)求,都新啟動(dòng)一個(gè)Netty客戶端類,對(duì)應(yīng)生成新啟動(dòng)兩個(gè)線程池,開銷較大,Netty的性能也完全沒(méi)有利用起來(lái)。由此,設(shè)計(jì)時(shí)采用單例模式,只生成一個(gè)AsyncHttpClient異步客戶端實(shí)例,所有的請(qǐng)求都是通過(guò)這個(gè)客戶端實(shí)例進(jìn)行發(fā)送的。
表1 配置項(xiàng)
2.4.3 編解碼類
因?yàn)镹etty消息處理采用事件驅(qū)動(dòng)模式,編解碼的工作在Pipeline中進(jìn)行。Netty已經(jīng)提供好了多種HTTP[6]的編解碼類,如HttpResponseDecoder(對(duì)HTTP響應(yīng)進(jìn)行解碼工作),HttpRequestEncoder(對(duì)HTTP請(qǐng)求進(jìn)行編碼工作)。
在Pipeline中除去編解碼類,剩下最主要的就是需要我們自己定義的響應(yīng)處理邏輯類HttpResponse Handler,該類需要繼承SimpleChannelUpstream Handler類。經(jīng)過(guò)Decoder解碼后的HTTP響應(yīng)消息會(huì)傳遞到處理邏輯類,方便我們響應(yīng)進(jìn)行操作。
這里采用了一種巧妙的設(shè)計(jì)思路,讓 Netty的啟動(dòng)類NettyHttpClient繼承SimpleChannelUpstream Handler類,使得響應(yīng)的處理邏輯也可以通過(guò)Netty啟動(dòng)類配置。統(tǒng)一了API的使用。
獲取返回消息,定義了兩種方式:
(1) 通過(guò)默認(rèn)的handler處理邏輯,獲取結(jié)果時(shí),通過(guò)阻塞的方式,等待默認(rèn)handler得到結(jié)果后,將結(jié)果返回。
(2) 通過(guò)自定義的回調(diào)函數(shù),實(shí)現(xiàn)客戶端代碼的異步。當(dāng)有消息時(shí),會(huì)自動(dòng)通知客戶端。通過(guò)實(shí)現(xiàn)回調(diào)函數(shù)接口,可以對(duì)響應(yīng)進(jìn)行多種操作。
2.4.4 設(shè)計(jì)模式
本文在設(shè)計(jì)異步客戶端時(shí),參考了很多開源框架[5]的設(shè)計(jì)原理,采用了工廠模式、Builder模式和單例模式等方法實(shí)現(xiàn)[7]。
(1)工廠模式
使用工廠模式構(gòu)建配置類和啟動(dòng)類,如圖4所示,不同的框架實(shí)現(xiàn)不同的配置類。異步客戶端類根據(jù)配置類的類型,產(chǎn)生不同的異步客戶端啟動(dòng)類,進(jìn)行HTTP請(qǐng)求的發(fā)送。為以后擴(kuò)展其他的框架實(shí)現(xiàn)方式提供了接口支持。
(2)Builder模式
因?yàn)榕渲妙愑泻芏鄥?shù)需要配置,如果對(duì)每一種可能的情況都設(shè)置一個(gè)構(gòu)造器,工作量將是非常巨大的,而且可讀性很差。采用Builder模式,不直接生成需要的對(duì)象,而是讓客戶端利用所有必要的參數(shù)調(diào)用構(gòu)造器(類似于靜態(tài)工廠的方式),得到一個(gè)Builder對(duì)象。然后通過(guò)對(duì)Builder對(duì)象上調(diào)用類似于setter的方法,來(lái)設(shè)置每一個(gè)相關(guān)的可選參數(shù)。Builder是配置類的靜態(tài)成員類,通過(guò)調(diào)用Build方法來(lái)生成不可變的對(duì)象。
(3)單例模式
通過(guò)單例模式可以保證系統(tǒng)中一個(gè)類只有一個(gè)異步客戶端實(shí)例可以被外界訪問(wèn),從而方便對(duì)實(shí)例個(gè)數(shù)的控制并節(jié)約系統(tǒng)資源,避免了每次發(fā)送請(qǐng)求構(gòu)建線程池和銷毀線程池的開銷,高并發(fā)量下,功能得到了保障,性能得到了提高。
2.5 分析測(cè)試和結(jié)果
在完成了基于Netty的HTTP客戶端開發(fā)工作了,對(duì)幾種HTTP客戶端的性能進(jìn)行了簡(jiǎn)單的比較。比較了3種HTTP客戶端。2種異步客戶端,分別為本文實(shí)現(xiàn)的客戶端, HttpAsyncClient。1種同步客戶端,HttpClient。
圖4 工廠模式
通過(guò)跑10 000個(gè)HTTP的GET請(qǐng)求,得出結(jié)果:使用異步的NettyHttpClient共用時(shí)間5 972 ms,使用異步的HttpAsyncClient共用時(shí)間9 021 ms,使用同步的HttpClient共用時(shí)間29 310 ms。從測(cè)試結(jié)果分析,發(fā)現(xiàn)異步客戶端比同步客戶端效率要高很多,而基于Netty實(shí)現(xiàn)的異步HTTP客戶端效率要比apache要高。
本文所述的基于Netty的異步HTTP客戶端已經(jīng)完成。配置類提供了25個(gè)配置項(xiàng),包括Netty啟動(dòng)的配置項(xiàng),以及HTTP請(qǐng)求的配置項(xiàng)。同時(shí)提供同步和異步HTTP請(qǐng)求,包括GET, POST, DELETE, PUT。還提供發(fā)送單向的HTTPS請(qǐng)求的功能。目前,本客戶端已經(jīng)應(yīng)用在實(shí)驗(yàn)室多個(gè)項(xiàng)目中。尤其在消息解析引擎中,作為必不可少的一部分。
本文設(shè)計(jì)并實(shí)現(xiàn)的異步HTTP客戶端,其設(shè)計(jì)思路借鑒了很多開源框架的設(shè)計(jì)原則,始終本著使用簡(jiǎn)單,低耦合和高內(nèi)聚的軟件設(shè)計(jì)思路進(jìn)行的實(shí)現(xiàn)。采用的都是通用接口設(shè)計(jì),方便以后擴(kuò)展。
雖然本客戶端已經(jīng)滿足了項(xiàng)目中的基本需求,但是還有部分功能沒(méi)有實(shí)現(xiàn),下一步需要進(jìn)一步研究的問(wèn)題是完善本客戶端的功能和配置項(xiàng)。同時(shí),考慮到不同的異步框架性能和實(shí)現(xiàn)原理的差異性,需要將其他的異步框架也融入進(jìn)去,使得用戶可以選擇性使用不同的底層實(shí)現(xiàn),來(lái)滿足自己不同的功能或者性能需求。
[1] 鄭建軍. Java在高并發(fā)網(wǎng)絡(luò)編程中的應(yīng)用[J]. 電腦編程技巧與維護(hù),2013(18):50-52.
[2] Goetz B. Java并發(fā)編程實(shí)戰(zhàn)[M]. 北京:機(jī)械工業(yè)出版社華章公司,2012.
[3] JBoss. Netty project[CP/OL]. [2012-4-1]. http://netty.io/
[4] Hitchens R. Java NIO[M]. USA: O'Reilly Media, 2002.
[5] Jfarcand. The Async Http Client[CP/OL]. [2012-3-16]. http:// sonatype.github.io/async-http-client/
[6] Gourley D, Totty B. HTTP權(quán)威指南[M]. 北京:人民郵電出版社,2012.
[7] 梅特斯克. Java設(shè)計(jì)模式[M]. 北京:人民郵電出版社,2007.
Design and implementation of HTTP client based on Netty
JIN Zhi-guo1,2, LI Wei1,2
(1 State Key Laboratory of Networking and Switching Technology, Beijing University of Posts and Telecommunications, Beijing 100876, China; 2 EBUPT Information Technology Co., Ltd., Beijing 100191, China)
Open Mobile Platform is an Open Platform providing ability of telecommunication. It provides developers with rich business abilities, including: SMS, MMS, maps, positioning, etc. The access system of Open Mobile Platform requires asynchronous processing HTTP requests for the client, and various systems of the platform also need to use a large number of asynchronous HTTP requests to send message. Netty is an asynchronous event-driven network applications open source framework, which is used for rapid development of maintainable high performance, high scalability protocol servers and clients. This paper briefy describes the design idea and concrete implementation of using open source framework Netty for providing a convenient and easy http client which can send an asynchronous HTTP request.
Netty framework; NIO technology; asynchrony; HTTP client
TN915
A
1008-5599(2014)01-0084-05
2013-12-05
國(guó)家973計(jì)劃項(xiàng)目(No. 2013CB329102);國(guó)家自然科學(xué)基金資助項(xiàng)目(No. 61372120,61271019, 61101119, 61121001, 61072057, 60902051);長(zhǎng)江學(xué)者和創(chuàng)新團(tuán)隊(duì)發(fā)展計(jì)劃資助(No. IRT1049);北京市支持中央高校共建項(xiàng)目——青年英才計(jì)劃。