《Netty 实战》Netty In Action中文版 第2章——你的第一款Netty应用程序(一)
第2章 你的第一款Netty應用程序
本章主要內(nèi)容
- 設(shè)置開發(fā)環(huán)境
- 編寫Echo服務器和客戶端
- 構(gòu)建并測試應用程序
在本章中,我們將展示如何構(gòu)建一個基于Netty的客戶端和服務器。應用程序很簡單:客戶端將消息發(fā)送給服務器,而服務器再將消息回送給客戶端。但是這個練習很重要,原因有兩個。
首先,它會提供一個測試臺,用于設(shè)置和驗證你的開發(fā)工具和環(huán)境,如果你打算通過對本書的示例代碼的練習來為自己將來的開發(fā)工作做準備,那么它將是必不可少的。
其次,你將獲得關(guān)于Netty的一個關(guān)鍵方面的實踐經(jīng)驗,即在前一章中提到過的:通過ChannelHandler來構(gòu)建應用程序的邏輯。這能讓你對在第3章中開始的對Netty API的深入學習做好準備。
2.1 設(shè)置開發(fā)環(huán)境
要編譯和運行本書的示例,只需要JDK和Apache Maven這兩樣工具,它們都是可以免費下載的。
我們將假設(shè),你想要搗鼓示例代碼,并且想很快就開始編寫自己的代碼。雖然你可以使用純文本編輯器,但是我們?nèi)匀粡娏业亟ㄗh你使用用于Java的集成開發(fā)環(huán)境(IDE)。
2.1.1 獲取并安裝Java開發(fā)工具包
你的操作系統(tǒng)可能已經(jīng)安裝了JDK。為了找到答案,可以在命令行輸入:
javac -version如果得到的是javac 1.7……或者1.8……,則說明已經(jīng)設(shè)置好了并且可以略過此步[1]。
否則,請從http://java.com/en/download/manual.jsp處獲取JDK第8版。請留心,需要下載的是JDK,而不是Java運行時環(huán)境(JRE),其只可以運行Java應用程序,但是不能夠編譯它們。該網(wǎng)站為每個平臺都提供了可執(zhí)行的安裝程序。如果需要安裝說明,可以在同一個網(wǎng)站上找到相關(guān)的信息。
建議執(zhí)行以下操作:
- 將環(huán)境變量JAVA_HOME設(shè)置為你的JDK安裝位置(在Windows上,默認值將類似于C:\Program Files\Java\jdk1.8.0_121);
- 將%JAVA_HOME%\bin(在Linux上為${JAVA_HOME}/bin)添加到你的執(zhí)行路徑。
2.1.2 下載并安裝IDE
下面是使用最廣泛的Java IDE,都可以免費獲取:
- Eclipse—— www.eclipse.org;
- NetBeans—— www.netbeans.org;
- Intellij IDEA Community Edition—— www.jetbrains.com。
所有這3種對我們將使用的構(gòu)建工具Apache Maven都擁有完整的支持。NetBeans和Intellij IDEA都通過可執(zhí)行的安裝程序進行分發(fā)。Eclipse通常使用Zip歸檔文件進行分發(fā),當然也有一些自定義的版本包含了自安裝程序。
2.1.3 下載和安裝Apache Maven
即使你已經(jīng)熟悉Maven了,我們?nèi)匀唤ㄗh你至少大致瀏覽一下這一節(jié)。
Maven是一款廣泛使用的由Apache軟件基金會(ASF)開發(fā)的構(gòu)建管理工具。Netty項目以及本書的示例都使用了它。構(gòu)建和運行這些示例并不需要你成為一個Maven專家,但是如果你想要對其進行擴展,我們推薦你閱讀附錄中的Maven簡介。
你需要安裝Maven嗎
?
Eclipse和NetBeans[2]自帶了一個內(nèi)置的Maven安裝包,對于我們的目的來說開箱即可工作得良好。如果你將要在一個擁有它自己的Maven存儲庫的環(huán)境中工作,那么你的配置管理員可能就有一個預先配置好的能配合它使用的Maven安裝包。
在本書中文版出版時,Maven 的最新版本是3.3.9。你可以從http://maven.apache.org/ download.cgi下載適用于你的操作系統(tǒng)的tar.gz或者zip歸檔文件[3]。安裝很簡單:將歸檔文件的所有內(nèi)容解壓到你所選擇的任意的文件夾(我們將其稱為<安裝目錄>)。這將創(chuàng)建目錄<安裝目錄>\apache-maven-3.3.9。
和設(shè)置Java環(huán)境一樣:
- 將環(huán)境變量M2_HOME設(shè)置為指向<安裝目錄>\apache-maven-3.3.9;
- 將%M2_HOME%\bin(或者在Linux上為${M2_HOME}/bin)添加到你的執(zhí)行路徑。
這將使得你可以通過在命令行執(zhí)行mvn.bat(或者mvn)來運行Maven。
2.1.4 配置工具集
如果你已經(jīng)按照推薦設(shè)置好了環(huán)境變量JAVA_HOME和M2_HOME,那么你可能會發(fā)現(xiàn),當你啟動自己的IDE時,它已經(jīng)發(fā)現(xiàn)了你的Java和Maven的安裝位置。如果你需要進行手動配置,我們所列舉的所有的IDE版本在Preferences或者Settings下都有設(shè)置這些變量的菜單項。相關(guān)的細節(jié)請查閱文檔。
這就完成了開發(fā)環(huán)境的配置。在接下來的各節(jié)中,我們將介紹你要構(gòu)建的第一個Netty應用程序的詳細信息,同時我們將更加深入地了解該框架的API。之后,你就能使用剛剛設(shè)置好的工具來構(gòu)建和運行Echo服務器和客戶端了。
2.2 Netty客戶端/服務器概覽
圖2-1從高層次上展示了一個你將要編寫的Echo客戶端和服務器應用程序。雖然你的主要關(guān)注點可能是編寫基于Web的用于被瀏覽器訪問的應用程序,但是通過同時實現(xiàn)客戶端和服務器,你一定能更加全面地理解Netty的API。
圖2-1 Echo客戶端和服務器
雖然我們已經(jīng)談及到了客戶端,但是該圖展示的是多個客戶端同時連接到一臺服務器。所能夠支持的客戶端數(shù)量,在理論上,僅受限于系統(tǒng)的可用資源(以及所使用的JDK版本可能會施加的限制)。
Echo客戶端和服務器之間的交互是非常簡單的;在客戶端建立一個連接之后,它會向服務器發(fā)送一個或多個消息,反過來,服務器又會將每個消息回送給客戶端。雖然它本身看起來好像用處不大,但它充分地體現(xiàn)了客戶端/服務器系統(tǒng)中典型的請求-響應交互模式。
我們將從考察服務器端代碼開始這個項目。
2.3 編寫Echo服務器
所有的Netty服務器都需要以下兩部分。
- 至少一個ChannelHandler——該組件實現(xiàn)了服務器對從客戶端接收的數(shù)據(jù)的處理,即它的業(yè)務邏輯。
- 引導——這是配置服務器的啟動代碼。至少,它會將服務器綁定到它要監(jiān)聽連接請求的端口上。
在本小節(jié)的剩下部分,我們將描述Echo服務器的業(yè)務邏輯以及引導代碼。
2.3.1 ChannelHandler和業(yè)務邏輯
在第1章中,我們介紹了Future和回調(diào),并且闡述了它們在事件驅(qū)動設(shè)計中的應用。我們還討論了ChannelHandler,它是一個接口族的父接口,它的實現(xiàn)負責接收并響應事件通知。在Netty應用程序中,所有的數(shù)據(jù)處理邏輯都包含在這些核心抽象的實現(xiàn)中。
因為你的Echo服務器會響應傳入的消息,所以它需要實現(xiàn)ChannelInboundHandler接口,用來定義響應入站事件的方法。這個簡單的應用程序只需要用到少量的這些方法,所以繼承Channel-InboundHandlerAdapter類也就足夠了,它提供了ChannelInboundHandler的默認實現(xiàn)。
我們感興趣的方法是:
- channelRead()——對于每個傳入的消息都要調(diào)用;
- channelReadComplete()——通知ChannelInboundHandler最后一次對channel-Read()的調(diào)用是當前批量讀取中的最后一條消息;
- exceptionCaught()——在讀取操作期間,有異常拋出時會調(diào)用。
該Echo服務器的ChannelHandler實現(xiàn)是EchoServerHandler,如代碼清單2-1所示。
代碼清單2-1 EchoServerHandler
@Sharable ?--- 標示一個Channel- Handler可以被多個Channel安全地共享 public class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf in = (ByteBuf) msg;System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8)); ?--- 將消息記錄到控制臺 ctx.write(in); ?--- 將接收到的消息寫給發(fā)送者,而不沖刷出站消息}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); ?--- 將未決消息沖刷到遠程節(jié)點,并且關(guān)閉該Channel}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) {cause.printStackTrace(); ?--- 打印異常棧跟蹤ctx.close(); ?--- 關(guān)閉該Channel} }ChannelInboundHandlerAdapter有一個直觀的API,并且它的每個方法都可以被重寫以掛鉤到事件生命周期的恰當點上。因為需要處理所有接收到的數(shù)據(jù),所以你重寫了channelRead()方法。在這個服務器應用程序中,你將數(shù)據(jù)簡單地回送給了遠程節(jié)點。
重寫exceptionCaught()方法允許你對Throwable的任何子類型做出反應,在這里你記錄了異常并關(guān)閉了連接。雖然一個更加完善的應用程序也許會嘗試從異常中恢復,但在這個場景下,只是通過簡單地關(guān)閉連接來通知遠程節(jié)點發(fā)生了錯誤。
如果不捕獲異常,會發(fā)生什么呢
?
每個Channel都擁有一個與之相關(guān)聯(lián)的ChannelPipeline,其持有一個ChannelHandler的實例鏈。在默認的情況下,ChannelHandler會把對它的方法的調(diào)用轉(zhuǎn)發(fā)給鏈中的下一個Channel-Handler。因此,如果exceptionCaught()方法沒有被該鏈中的某處實現(xiàn),那么所接收的異常將會被傳遞到ChannelPipeline的尾端并被記錄。為此,你的應用程序應該提供至少有一個實現(xiàn)了exceptionCaught()方法的ChannelHandler。(6.4節(jié)詳細地討論了異常處理)。
除了ChannelInboundHandlerAdapter之外,還有很多需要學習的ChannelHandler的子類型和實現(xiàn),我們將在第6章和第7章中對它們進行詳細的闡述。目前,請記住下面這些關(guān)鍵點:
- 針對不同類型的事件來調(diào)用ChannelHandler;
- 應用程序通過實現(xiàn)或者擴展ChannelHandler來掛鉤到事件的生命周期,并且提供自定義的應用程序邏輯;
- 在架構(gòu)上,ChannelHandler有助于保持業(yè)務邏輯與網(wǎng)絡(luò)處理代碼的分離。這簡化了開發(fā)過程,因為代碼必須不斷地演化以響應不斷變化的需求。
- 轉(zhuǎn)載自 并發(fā)編程網(wǎng) - ifeve.com
總結(jié)
以上是生活随笔為你收集整理的《Netty 实战》Netty In Action中文版 第2章——你的第一款Netty应用程序(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Andriod开发技巧——Fragmen
- 下一篇: MongoDB 小试牛刀