基本概念
概念
Netty是由JBOSS提供的一個Java NIO開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。可實現的功能見下圖:
Netty vs Mina vs Grizzly
- Mina的設計理念最爲優雅,Netty和Mina的主導作者是同一人。
- Mina出自於Apache,Netty出身於商業開源大亨Jboss,而Grizzly則出身於土鱉Sun公司。
Netty作爲NIO框架的優勢
- API使用簡單
- 功能強大,預置了多種編解碼功能,支持多種主流協議
- 定製能力強,可以通過ChannelHandler對通信框架進行靈活擴展
- 性能高
- 社區活躍
相關類
ByteBuf
使用數據傳輸,需要使用到緩衝區。ByteBuf是個Byte數組的緩衝區,基本功能和JDK的ByteBuffer一致。
- 兩個指針維持緩衝區的讀寫操作:readerIndex標識讀索引,writerIndex標識寫索引。
- 順序讀操作(read)
- 順序寫操作(write)
- Discardable bytes,重用緩衝區,因爲緩衝區的分配和釋放是個耗時的操作。
- clear操作,並不是清空緩衝區內容本身,而是將readerIndex和writerIndex還原爲初始分配值。
- Mark和Rest,回滾操作實現,備份readerIndex、WriterIndex
- 查找操作
- Derived buffers,類似於數據庫的視圖。duplicate、copy、slice
- 轉換爲標準的ByteBuffer
- 隨機讀寫(set和get)
ByteBuf類圖
堆內存:內存的分配和回收速度快,被JVM自動回收。
- 直接內存:與Socket Channel通信,少了一次內存複製,速度比堆快。
- 基於對象池的ByteBuf和普通ByteBuf
- ByteBuf相關輔助類
- ByteBufHolder,是ByteBuf的容器,對ByteBuf進行包裝和抽象。
- ByteBufAllocator,字節緩衝區分配器:基於內存池和普通的。
- CompositeByteBuf,將多個ByteBuf的實例組裝到一起,形成一個統一的視圖。
- ByteBufUtil,提供了一些靜態方法用於操作ByteBuf對象。例如字符串的編碼和解碼。
Channel 和 Unsafe
Channel用於異步I/O操作,Unsafe是個內部接口,聚合在Channel中協助進行網絡讀寫相關的操作。ChannelPipeline和ChannelHandler
類似於Servlet和Filter過濾器,是職責鏈模式的一種變形,主要是爲了方便事件的攔截和用戶業務邏輯的定製。將Channel的數據管道抽象爲ChannelPipeline,消息在ChannelPipeline中流動和傳遞,ChannelPipeline持有I/O事件攔截器ChannelHandler的鏈表,由ChannelHandler對I/O事件進行攔截和處理,可以方便地通過新增和刪除ChannelHandler來實現不同的業務邏輯定製。
- inbound事件通常由I/O線程觸發(fire),outbound事件通常由用戶主動發起的網絡I/O操作。
- 用戶不需要自己創建pipeline,因爲使用ServerBootstrap和Bootstrap啓動後,Netty會爲每個Channel連接創建一個獨立的pipeline,用戶只需要將自定義的攔截器加入到pipeline中即可。
- ChannelHandlerAdapter
- ByteToMessageDecoder:ByteBuf解碼爲業務POJO對象
- MessageToMessageDecoder:Netty的二次解碼器,將一個對象二次解碼爲其他對象。
- LengthFieldBasedFrameDecoder:半包解碼器
- 類圖
EventLoop 和EventLoopGroup
線程模型,Netty框架的主要線程就是I/O線程,被精心設計,即提升了框架的併發性性能,又在很大程度避免鎖,局部實現了無鎖化機制。
- Reactor單線程模型,異步非阻塞I/O,理論上一個線程可以獨立處理所有I/O相關的操作。
Reactor多線程模型
- Acceptor線程:監聽服務端,接受客戶端的TCP連接請求,專門的NIO線程。
- 網絡I/O操作
- 一個NIO線程可以處理N條鏈路,但是一個鏈路只對應一個NIO線程,防止併發操作的問題。 -
主從Reactor多線程模型
NIO線程池的引入。Netty線程模型
並不是一成不變,取決於用戶的啓動參數配置。
- NioEventLoop,需要處理I/O事件,所以必須聚合多路複用其Selector。
- Future和Promise
Future用於獲取異步操作的結果
ChannelFuture有兩種狀態:upcompleted和completed;ChannelFuture可以同時增加一個或多個GenericFutureListener。需要注意的是,不要在ChannelHandler中調用ChannelFuture的await()方法,否則會導致死鎖。如果I/O線程和用戶線程是同一個線程,就會導致I/O線程等待自己通知操作系統,屬於自己把自己掛死。
Promise是可寫的Future,對Future進行擴展,用於設置I/O操作的結果。強烈建議通過增加監聽器Listener的方式接受異步I/O操作結果的通知,而不是調用wait或者sync阻塞用戶線程。