有關Netty,咱們能夠從兩個視角來討論Netty:類庫的視角以及框架的視角,對於使用Netty編寫高效的、可重用的和可維護的代碼來講,二者缺一不可。git
Netty解決了兩個響應的關注領域,能夠大體標誌爲技術的和體系結構的。設計模式
它基於Java NIO的異步和事件驅動的實現,保證了高負載下應用程序性能的最大化和可伸縮性。其次,Netty也包含了一組設計模式,將應用程序邏輯從網絡層解耦,簡化開發過程,同時也最大限度地提升了可測試性、模塊化以及代碼的可重用性。服務器
Netty網絡抽象的表明:網絡
——Channel:Socket多線程
——EventLoop:控制流、多線程處理、併發併發
——ChannelFuture:異步通知框架
一、Channel接口異步
Netty的Channel接口所提供的的API,大大下降了直接使用Socket類的複雜性,此外,Channel也擁有許多預約義的、專門化實現的普遍類層次結構的根。模塊化
——EmbeddedChanneloop
——LocalServerChannel
——NioDatagramChannel
——NioSctpChannel
——NioSocketChannel
二、EventLoop接口
EventLoop定義了Netty的核心抽象,用於處理鏈接的生命週期中所發生的事件。圖3-1在高層次上說明了Channel、EventLoop、Thread以及EventLoopGroup之間的關係。
——EventLoopGroup包含一個或者多個EventLoop
——一個EventLoop在它的生命週期內只和一個Thread綁定
——全部由EventLoop處理的I/O事件都將在它專有的Thread上被處理
——一個Channel在它的生命週期內只註冊於一個EventLoop
——一個EventLoop可能會被分配給一個或多個Channel
注意,在這種設計中,一個給定Channel的I/O操做都是由相同的Thread執行的,實際上消除了對於同步的須要。
三、ChannelFuture接口
Netty中全部的I/O操做都是異步的,由於一個操做可能不會當即返回,因此咱們須要一種用於在以後的某個時間點肯定其結果的方法,爲此,Netty提供了ChannelFuture接口,其addListener()方法註冊了一個ChannelFutureListener,以便在某個操做完成時(不管是否成功)獲得通知。
關於ChannelFuture的更多討論——能夠將ChannelFuture看做是未來要執行的結果的佔位符,它究竟何時被執行則可能取決於若干的因素,所以不可能準確地預測,可是能夠確定的是它將會被執行,此外,全部屬於同一個Channel的操做都被保證其將以它們被調用的順序被執行。
四、ChannelHandler接口
Netty的主要組件是ChannelHandler,它充當了全部處理入站和出站數據的應用程序邏輯的容器。由於ChannelHandler的方法是由網絡事件觸發的。事實上,ChannelHandler可專門用於幾乎任何類型的動做,例如將數據從一種格式轉換爲另外一種格式,或者處理轉換過程當中所拋出的異常。
例如,ChannelInboundHandler 是一個你將會常常實現的子接口,這種類型的ChannelHandler接收入站事件和數據,這些數據隨後將會被你的應用程序的業務邏輯所處理。當你要給鏈接的客戶端發送響應時,也能夠從ChannelInboundHandler沖刷數據。你的應用程序的業務邏輯一般駐留在一個或者多個ChannelInboundHandler中。
五、ChannelPipeline接口
ChannelPipeline爲ChannelHandler鏈提供了容器,並定義了用於在該鏈上傳播入站和出站事件流的API。當Channel被建立時,它會被自動地分配到它專屬的ChannelPipeline。
——一個ChannelInitializer的實現被註冊到了ServerBootstrap中; ——當ChannelInitializer.initChannel()方法被調用時,ChannelInitializer將在ChannelPipeline中安裝一組自定義的ChannelHandler; ——ChannelInitializer將它本身從ChannelPipeline中移除;
ChannelHandler是專爲支持普遍的用途而設計的,能夠將它看做是處理往來ChannelPipeline事件(包括數據)的任何代碼的通用容器。圖2-3說明了這一點,其展現了從ChannelHandler派生的ChannelInboundHandler和ChannelOutboundHandler接口。
使得事件流經ChannelPipeline是ChannelHandler的工做,它們是在應用程序的初始化或者引導階段被安裝的,這些對象接收事件、執行它們所實現的處理邏輯,並將數據傳遞給鏈中的下一個ChannelHandler。它們的執行順序是由它們被添加的順序所決定的。ChannelPipeline就是ChannelHandler的編排順序。
圖3-3說明了一個Netty應用程序中入站和出站數據流之間的區別。從一個客戶端應用程序的角度來看,若是事件的運動方向是從客戶端到服務器端,那麼咱們稱這些事件爲出站,反之則稱爲入站。
由上圖可看出入站和出站ChannelHandler能夠被安裝到同一個ChannelPipeline中,事件的讀取將從ChannelPipeline的頭部開始流動,並被傳遞給第一個ChannelInboundHandler,這個ChannelHandler不必定會實際修改數據,具體取決於它的具體功能,在以後,數據將會傳遞給鏈中的下一個ChannelInboundHandler,最終,數據將會到達ChannelPipeline的尾端,即全部處理結束。
出站事件也相同的流程,在到達鏈的頭部以後,出站數據將會到達網絡傳輸層,這裏顯示爲Socket,一般狀況下,這將觸發一個寫操做。
關於入站和出站ChannelHandler的更多討論
經過使用做爲參數傳遞到每一個方法的ChannelHandlerContext,事件能夠被傳遞給當前ChannelHandler鏈中的下一個ChannelHandler。由於你有時會忽略那些不感興趣的事件,因此Netty提供了抽象基類ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter。經過調用ChannelHandlerContext上的對應方法,每一個都提供了簡單地將事件傳遞給下一個ChannelHandler的方法的實現,隨後,你能夠經過重寫你所感興趣的那些方法來擴展這些類。
雖然ChannelInboundHandler和ChannelOutboundHandler都擴展自ChannelHandler,可是Netty能區分ChannelInboundHandler實現和ChannelOutboundHandler實現,並確保數據只會在具備相同定向類型的兩個ChannelHandler之間傳遞。
當ChannelHandler被添加到ChannelPipeline時,它將被分配一個ChannelHandlerContext,其表明了ChannelHandler和ChannelPipeline之間的綁定。雖然這個對象能夠被用於獲取底層的Channel,可是它主要仍是被用於寫出站數據。
在Netty中,有兩種發送消息的方式。你能夠直接寫到Channel中,也能夠寫到ChannelHandler相關聯的ChannelHandlerContext對象中,前一種方式將會致使消息從ChannelPipeline的尾端開始流動,然後者將致使消息從ChannelPipeline中的下一個ChannelHandler開始流動。
六、深刻了解ChannelHandler
有許多不一樣類型的ChannelHandler,它們各自的功能主要取決於它們的超類。Netty以適配器類的形式提供了大量默認的ChannelHandler實現,其旨在簡化應用程序處理邏輯的開發過程,ChannelPipeline中的每一個ChannelHandler將負責把事件轉發到鏈中的下一個ChannelHandler,這些適配器將自動執行這個操做,因此你能夠只重寫那些你想要特殊處理的方法和事件。
爲何須要適配器類
有些適配器類能夠將編寫自定義的ChannelHandler所須要的努力降到最低限度,由於他們提供了定義在對應接口中的全部方法的默認實現。
——ChannelHandlerAdapter
——ChannelInboundHandlerAdapter
——ChannelOutboundHandlerAdapter
——ChannelDuplexHandler
七、編碼器和解碼器
當你經過Netty發送或者接收一個消息的時候,就將會發生一次數據轉換。入站就解碼爲Java對象,出站就編碼爲字節。(網絡數據老是一系列的字節)
全部由Netty提供的編碼器、解碼器適配器類都實現了ChannelOutboundHandler或者ChannelInboundHandler接口。
你將會發現對於入站數據,channelRead方法、事件已經被重寫了,對於每一個從入站Channel讀取消息,這個方法都將會被調用,隨後,它將調用由預置解碼器所提供的的decode()方法,並將已解碼的字節轉發給ChannelPipeline中的下一個ChannelInboundHandler。
出站消息的模式是相反方向的:編碼器將消息轉換爲字節,並將它們轉發給下一個ChannelOutboundHandler。
八、抽象類SimpleChannelInboundHandler
最多見的狀況是,你的應用程序會利用一個ChannelHandler來接收解碼消息,並對該數據應用業務邏輯。要建立一個這樣的ChannelHandler,你只須要擴展基類SimpleChannelInboundHandler,其中T是你要處理的消息的Java類型,在這個ChannelHandler中,你將須要重寫基類的一個或者多個方法,而且獲取一個到ChannelHandlerContext的引用,這個引用將做爲輸入參數傳遞給ChannelHandler的全部方法。
在這種類型的ChannelHandler中,最重要的方法是channelRead0(ChannelHandlerContext,T)。除了要求不要阻塞當前的I/O線程以外,其具體實現徹底取決於你。
九、引導
Netty的引導類爲應用程序的網絡層配置提供了容器,這涉及將一個進程綁定到某個指定的端口,或者將一個進程鏈接到另外一個運行在某個指定主機的指定端口上的進程。
「服務器」和「客戶端」實際上表示了不一樣的網絡行爲:是監聽傳入的鏈接仍是創建到一個或者多個進程的鏈接。
面向鏈接的協議:請記住,嚴格來講,「鏈接」這個屬於僅適用於面向鏈接的協議,如TCP、其保證了兩個鏈接端點之間消息的有序傳遞。
所以,有兩種類型的引導:一種用於客戶端(簡單稱爲Bootstrap),而另外一種(ServerBootstrap)用於服務器。不管你的應用程序使用哪些協議或者處理哪一種類型的數據,惟一決定它使用哪一種引導類的是它是做爲一個客戶端仍是做爲一個服務器。
這兩種類型的引導類之間的第一個區別已經討論過了:ServerBootstrap將綁定到一個端口,由於服務器必需要監聽鏈接,而Bootstrap則由想要鏈接到遠程節點的客戶端應用程序所使用的。
第二個區別可能更加明顯,引導一個客戶端只須要一個EventLoopGroup,可是一個ServerBootstrap則須要兩個(也能夠是同一個實例),爲何呢?
由於服務器須要兩組不一樣的Channel。第一組將只包含一個ServerChannel,表明服務器自身的已綁定到某個本地端口的正在監聽的套接字。而第二組將包含全部已建立的用來處理傳入客戶端鏈接(對於每一個服務器已經接受的鏈接都有一個)的Channel。
與ServerChannel相關聯的EventLoopGroup將分配一個負責爲傳入鏈接請求建立Channel的EventLoop。一旦鏈接被接收,第二個EventLoopGroup就會給它的Channel分配一個EventLoop。