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