Netty4.x分析


官網定義: netty是一個異步、事件驅動的網絡應用框架,用於快速開發可維護的、高性能的服務端和客戶端程序。

原理分析 

Architecture Overview

網絡模型:netty采用了Reactor設計模式,Reactor設計可分三種:

單線程版本,如圖:

 

學C的朋友會知道IO多路復用,我感覺和這個Reactor模式差不多,Reactor收到新連接調用acceptor的accept,返回的SocketChannel會注冊到Reactor里,當連接可讀或者可寫時,分發一個handler處理。

多線程版本,如圖:

 

處理部分增加了線程池。

Multi-Reactor版本:

 

監聽端口注冊到mainReactor里,有連接,調用accept,返回的連接注冊到subReactor里,subReactor只負責讀寫,處理部分交給線程池。

Netty采用的方式類似於第三種,Netty3.6里mainReactor對應Boss類,subReactord對應NioWorker類;4.x里是實現EventLoopGroup接口的某個類,如NioEventLoopGroup(multithreaded event loop that handles I/O operation),EventLoopGroup相當於管理EventLoop的線程池,thread數量是可以配置的,echoServer例子中:

42,43行就是boss和worker了,ServerBootstrap是設置服務器的幫助類。

47行用NioServerSocketChannel類說明后面會用它去實例channel來接受incoming連接。

48行option方法可以指定Channel實現的方式。

50行:subReactor監聽的channel來事件了,處理方法要通過childHandler方法指定,這是需要我們實現的,childHandler方法的參數是ChannelHandler接口的某個類,然后回調;拿FactorialServerInitializer舉例,層次關系如圖:

ChannelInitializer用來配置channel,這里要實現抽象類ChannelInitializer里的initChannel方法,意味着要在initChannel方法里配置pipeline。

我們觀察EventLoopGroup類,如下圖所示:

 

EventExecutor和EventLoopGroup都包含通用的 event loop API;EventLoopGroup有register方法,提供向其注冊channel,返回ChannelFuture;

Netty Pipeline:

每個channel都有自己的pipeline,channel創建則對應的pipeline自動創建,下圖顯示了IO事件如何通過ChannelHandler在ChannelPipeline中處理的:

 

在pipeline里,每個stage運行一個InboundHandler或OutboundHandler,設計過MIPS經典五段pipeline的朋友應該知道鎖存器設計,這里對應ChannelHandlerContext,ChannelHandlerContext可以通知ChannelPipeline里下一個ChannelHandler工作,並把事件流傳給下一個ChannelHandler,也可以動態修改它所屬的ChannelPipeline;

Inbound事件流傳遞方法:

Outbound事件流傳遞方法:

 我們看下官網的例子,io.netty.example.factorial 這個包,Pipeline部分(服務端):

 

當Socket.read()發生時,handler處理事件的順序是:BigIntegerDecoder->FactorialServeHandler

當Socket.write()發生時,handler處理事件的順序是:NumberEncoder

ChannelInboundHandlerAdapter的方法:

ChannelOutboundHandlerAdapter的方法:

關於Pipeline的一些說明:並不是一個階段執行完了,才去執行下一個階段,而是每個Handler有對應的事件處理方法(如上ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter中的方法),當這個Handler接收到了某事件,就會調用這個事件處理方法,然后會觸發下個Handler對應的事件處理方法,下面用自帶的io.netty.example.http.helloworld包驗證這個想法(在方法里插了輸出):

Socket.read():HttpServerCodec->HttpHelloWorldServerHandler;

Socket.write():HttpServerCodec

Zero Copy:

Netty里用到了ZC技術,這里是介紹ZeroCopy比較好的一篇文章,Netty里FileRegion就是用來支持ZeroCopy的接口,ZC在傳輸大文件時比較有優勢,把大文件指定到Channel上,直接傳輸,不經過Application層。

 Channel包:

Channel接口封裝了socket,提供IO操作(如read,write,connect,bind等)的組建,具體屬性、方法可參考文檔;

ChannelFuture接口繼承了io.netty.util.concurrent.Future,Netty中IO調用均是異步的,調用立即返回,返回結果記錄在ChannelFuture里,ChannelFuture隨着IO操作的開始而被創建,它的狀態可以是完成或者未完成,初始態是未完成,狀態如下:

我們可以通過向ChannelFuture里增加和刪除ChannelFutureListener(繼承GenericFutureListener)(通過addListener(s)、removeListener(s)方法),IO操作完成時觸發GenericFutureListener的operationComplete方法執行,這是異步的操作;我們也可以調用ChannelFuture的await方法阻塞Control Flow,直到ChannelFuture完成。

ChannelPipeline已在上文介紹;

Buffer包:

Netty使用自己的buffer API處理字節序列,而不是使用NIO自帶的buffer,這樣的定制有很多優勢吧,官方文檔是這么說的:在常見的網絡應用中,我們會有一些buffer,它們經常需要組裝成一個buffer,netty提供composite buffer,它允許你把已經存在的幾個buffer組合起來創建一個virtual buffer,不需要內存拷貝:

還有許多協議的MTU都是不確定的,Netty允許你創建動態大小的buffer,來降低內存開銷。

Channel讀的數據會寫到實現ByteBuf接口的某個類里,ByteBuf里數據滿了,會調用handler處理,io.netty.handler.codec包里面會有一些類把package frame,也就是收到的ByteBuf decode成Message,交給handler處理;ByteBuf提供Java nio緩存(ByteBuffer)類似的方法,ByteBuf接口的實現層次圖:

buffer包中有一個幫助類Unpooled,用於創建ByteBuf,所有的ByteBuf都是通過ByteBufAllocatore和UnpooledByteBufAllocator分配的,在Unpooled類里,默認的分配器是UnpooledByteBufAllocator,默認分配的ByteBuf類型是UnpooledHeapByteBuf;

HeapByteBuf是在Java堆上分配內存;DirectByteBuf用的是NIO的ByteBuffer,CompositeByteBuf是一個虛擬的buffer,將多種buffer合並成一個buffer;

細心的同學會看到PoolByteBuf和UnpooledByteBuf,PoolByteBuf是4.0新引入的,設計思想借鑒jemalloc(core:混合了slab分配器和buddy分配器),優點是減少內存碎片,Slab分配器是基於對象管理的,分配對象,直接從Slab系統里拿,無需再次初始化,釋放對象,則保留在Slab系統里,標記為臟,不需釋放,降低GC壓力;

 io.netty.handler.codec包:

ByteToMessageCodec類封裝了ByteToMessageDecoder和MessageToByteEncoder(都作為Pipeline Handler);我們先觀察ByteToMessageDecoder,其可以將流式的字節轉化成消息類型,他有一個成員cumulation(ByteBuf類型),收消息時會把收到的msg(ByteBuf類型)傳遞給cumulation,數據准備好后調用callDecode,callDecode進一步調用decode方法(具體分幀方法),這個方法交給子類實現;MessageToByteEncoder里的write方法同理,write里調用子類實現的encode方法將消息encode成ByteBuf,發送出去。

 

Netty in Twitter:

寫Netty的同學在twitter工作,twitter的新搜索架構Blender就是基於Netty的,如果您能翻牆,可以訪問這里,否則中文在這里,英文在這里,來了解Blender的架構。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM