Netty是一個非阻塞的,時間驅動的網絡框架。web
一個Netty程序開始於一個Bootstrap類,Bootstrap類是Netty提供的一個能夠經過簡單配置來設置或「引導」程序的重要的類。數據庫
Netty中設計了Handlers來處理特定的「event」和設置Netty中的事件,從而來處理多個協議和數據。能夠自定義Handler用來將Obeject轉換成byte[],或者將byte[]轉換成Object,或者定義handler處理異常。數組
ChannelInboundHandler用來接收消息,當程序須要返回信息時,在ChannelInboundHandler裏write/flush數據。能夠認爲程序的業務邏輯都是在ChannelInboundHandler裏處理的,業務邏輯的生命週期是在ChannelInboundHandler中。promise
鏈接客戶端或者綁定服務器端須要知道如何發送或接收消息,能夠經過不一樣類型的Handler來實現。Netty經過ChannelInitializer來配置Handler,ChannelInitializer經過ChannelPipeline來添加Handler。ChannelInitializer自己也是一個ChannelHander,在添加完其餘的Handler以後會本身從ChannelPipeline刪除本身。安全
Netty中全部的I/O操做都是異步的,Netty使用Futrures或者ChannelFutures來達到註冊監聽異步操做是否成功的目的。Furture註冊一個監聽,當操做成功或失敗時會通知,ChannelFutures封裝一個操做的相關信息,操做被執行時,會馬上返回ChannelFutures。服務器
Netty中的EventLoopGroup包含一個或多個EventLoop,而EventLoop就是一個Channel執行實際工做的線程。EventLoop老是綁定一個單一的線程,在其生命週期內不會改變。這保證了在Netty I/O操做中,程序不須要同步,由於一個指定通道的全部I/O始終由同一個線程來執行。不少Channel會共享同一個EventLoop。網絡
Bootstrap用來鏈接遠程主機,有1個EventLoopGroup(單例的)。框架
ServerBootstrap用來綁定本地端口,有2個EventLoopGroup(實際上使用的是相同的實例)。異步
ServerBootstrap在服務器端監聽一個端口,輪詢客戶端的「Bootstrap」或DatagramChannel是否鏈接服務器,一般須要調用「Bootstrap」類的connect()方法,可是也能夠先調用bind()再調用connect()進行鏈接,以後使用的Channel包含在bind()返回的ChannelFuture中。socket
一個ServerBootstrap能夠認爲有2個channel組,第一組包含一個單例的ServerChannel,表明持有了一個保存本地端口的socket;第二組包含全部的Channel,表明服務器已經接受了的連接。
上圖中EventLoopGroup A的惟一目的就是接收全部鏈接請求,而後交給EventLoopGroup B。
EventLoopGroup將是程序性能的瓶頸,由於時間循環忙於處理連接請求,沒有多餘的資源來處理業務邏輯,如有兩個EventLoops,即便是在高負載的狀況下,全部的鏈接也會被接收,由於EventLoop接收連接不會和已鏈接並進行業務邏輯處理的EventLoop共享資源。
ChannelHandler是一段執行業務邏輯的代碼,它們來來每每的經過ChannelPipeline.
Netty中有兩個方向的數據流,入站(ChannelInboundHandler)和出站(ChannelOutboundHandler),若數據是從應用程序到遠程主機,則是出站(outbound),若是是從遠程主機到應用程序則是入站(Inbound)。
ChannelPipleline是一個管理ChannelHandler的容器,ChannelHandler在程序Bootstrap階段被添加到ChannelPipleline中,被添加的順序將決定處理數據的順序,
訪問非堆緩衝區ByteBuf的數組會致使UnsupportedOperationException,可使用ByteBuf.hasArray()來檢查是否支持訪問數組。ByteBuf.hasArray()的原理
Direct Buffer(直接緩衝區)在堆以外直接分配內存。直接緩衝區不會佔用堆空間容量,使用時應該考慮到應用程序要使用的最大內存容量以及如何限制它。直接緩衝區在使用Socket傳遞數據時性能很好,由於若使用間接緩衝區,JVM會先將數據複製到直接緩衝區再進行傳遞。直接緩衝區不支持數組訪問數據。
複合緩衝區,咱們能夠建立多個不一樣的ByteBuf,而後提供一個這些ByteBuf組合的視圖。Netty提供了CompositeByteBuf類來處理複合緩衝區,CompositeByteBuf只是一個視圖,CompositeByteBuf.hasArray()老是返回false。一條消息由header和body兩部分組成,將header和body組裝成一條消息發送出去,可能body相同,只是header不一樣,使用
CompositeByteBuf就不用每次都從新分配一個新的緩衝區。
注意經過索引訪問時不會推動讀索引和寫索引,咱們能夠經過ByteBuf的readerIndex()或writerIndex()。ByteBuf提供兩個指針變量支付讀和寫操做,讀操做是使用readerIndex(),寫操做時使用writerIndex()。這和JDK的ByteBuffer不一樣,ByteBuffer只有一個方法來設置索引,因此須要使用flip()方法來切換讀和寫模式。
調用ByteBuf.clear()能夠設置readerIndex和writerIndex爲0,clear()不會清除緩衝區的內容,只是將兩個索引值設置爲0。請注意
ByteBuf.clear()與JDK的ByteBuffer.clear()的語義不一樣。ByteBuf.discardReadBytes()能夠用來清空ByteBuf中已讀取的數據,從而使ByteBuf有多餘的空間容納新的數據,可是
discardReadBytes()可能會涉及內存複製,由於它須要移動ByteBuf中可讀的字節到開始位置,這樣的操做會影響性能,通常在須要立刻釋放內存的時候使用收益會比較大。和discardReadBytes()相比,clear()是便宜的,由於clear()不會複製任何內存。
Netty使用reference-counting(引用計數)的時候知道安全釋放Buf和其餘資源,雖然知道Netty有效的使用引用計數,這都是自動完成的。
獲取ByteBufAllocator對象很容易,能夠從Channel的alloc()獲取,也能夠從ChannelHandlerContext的alloc()獲取。
永遠只有一個channelActive和channelInactive的狀態,由於一個通道在其生命週期內只能鏈接一次,以後就會被回收;從新鏈接,則是建立一個新的通道。
ChannelHandlerAdapter實現了父類的全部方法,基本上就是傳遞事件到ChannelPipeline中的下一個ChannelHandler直到結束ChannelInboundHandlerAdapter的channelRead方法處理完消息後不會自動釋放消息,若想自動釋放收到的消息,可使用SimpleChannelInboundHandler。
若是消息被消費而且沒有被傳遞到ChannelPipeline中的下一個ChannelOutboundHandler,那麼就須要調用ReferenceCountUtil.release(message)來釋放消息資源。一旦消息被傳遞到實際的通道,它會自動寫入消息或在通道關閉時釋放。重要的是要記得釋放致遠並通知ChannelPromise,若ChannelPromise沒有被通知可能會致使其中一個ChannelFutureListener不被通知去處理一個消息。例如,應當使用以下的方式:
public class DiscardOutBoundler extends ChannelOutboundHandlerAdapter{ @override public void write(ChannelHandlerContext cxt , Object msg, ChannlePromise promise) throws Exception{ ReferenceCountUtil.release(msg); promise.setSuccess(); } }
消息被編碼後解碼後會自動經過ReferenceCountUtil.release(message)釋放,若是不想釋放消息可使用ReferenceCountUtil.retain(message)
有時候須要從另外一個Channel引導客戶端,例如寫一個代理或須要從其餘系統檢索數據。從其餘系統獲取數據時比較常見的,有很 多Netty應用程序必需要和企業現有的系統集成,如Netty程序與內部系統進行身份驗證,查詢數據庫等。固然,你能夠建立一個新的引導,這樣作沒有什麼不妥,只是效率不高,由於要爲新建立的客戶端通道使用另外一個EventLoop,如 果須要在已接受的通道和客戶端通道之間交換數據則須要切換上下文線程。Netty對這方面進行了優化,能夠講已接受的通道經過 eventLoop(...)傳遞到EventLoop,從而使客戶端通道在相同的EventLoop裏運行。這消除了額外的上下文切換工做,由於EventLoop繼承 於EventLoopGroup。除了消除上下文切換,還能夠在不須要建立多個線程的狀況下使用引導。 一個EventLoop由一個線程執行,共享EventLoop能夠肯定全部的Channel都分配給同一線程的 EventLoop,這樣就避免了不一樣線程之間切換上下文。
比較麻煩的是建立通道後不得不手動配置每一個通道,爲了不這種狀況,Netty提供了ChannelOption來幫助引導配置。這些選項會 自動應用到引導建立的全部通道,可用的各類選項能夠配置底層鏈接的詳細信息,如通道「keep-alive(保持活躍)」或「timeout(超時)」的特性。Netty提供了通道屬性(channel attributes)。屬性能夠將數據和通道以一個安全的方式關聯,這些屬性只是做用於客戶端和服務器的通道。例如,例如客戶端請求web服務器應 用程序,爲了跟蹤通道屬於哪一個用戶,應用程序能夠存儲用的ID做爲通道的一個屬性。任何對象或數據均可以使用屬性被關聯到一個通道。使用ChannelOption和屬性可讓事情變得很簡單,例如Netty WebSocket服務器根據用戶自動路由消息,經過使用屬性,應用程序 能在通道存儲用戶ID以肯定消息應該發送到哪裏。應用程序能夠經過使用一個通道選項進一步自動化,給定時間內沒有收到消息將自動斷開鏈接。