Netty相關問題小結
Netty的特色
- Netty是一個高性能、異步事件驅動的NIO框架,它提供了對TCP、UDP和文件傳輸的支持
- Netty使用更高效的socket底層通訊方式epoll,對JAVA原生NIO空輪詢引發的cpu佔用飆升在內部進行了處理,避免了直接使用NIO的陷阱,簡化了NIO的處理方式
- 採用多種decoder/encoder支持,對TCP粘包/半包問題進行自動化處理
- 可以使用接受(bossGroup)/工做(workGroup)線程池,提升鏈接效率,對重連、心跳檢測的簡單支持
- 可配置IO線程數、TCP參數, TCP接收和發送緩衝區可使用直接內存代替堆內存,實現零拷貝,經過內存池的方式循環利用ByteBuf
- 經過引用計數器及時申請釋放再也不引用的對象,下降了GC頻率
- 使用單線程串行化的方式,高效的Reactor線程模型
- 大量使用了volitale、使用了CAS和原子類、線程安全類的使用、讀寫鎖的使用
Netty的零拷貝實現
Netty 的零拷貝主要包含三個方面:編程
- Netty 的接收和發送 ByteBuf 採用
DIRECT BUFFERS
,使用堆外直接內存進行 Socket 讀寫,不須要進行字節緩衝區的二次拷貝。若是使用傳統的堆內存(HEAP BUFFERS
)進行 Socket 讀寫,JVM 會將堆內存 Buffer 拷貝一份到直接內存中,而後才寫入 Socket 中。相比於堆外直接內存,消息在發送過程當中多了一次緩衝區的內存拷貝
- Netty 提供了組合 Buffer 對象,能夠聚合多個 ByteBuf 對象,用戶能夠像操做一個 Buffer 那樣方便的對組合 Buffer 進行操做,避免了傳統經過內存拷貝的方式將幾個小 Buffer 合併成一個大的 Buffer
- Netty 的文件傳輸採用了
transferTo
方法,它能夠直接將文件緩衝區的數據發送到目標 Channel,避免了傳統經過循環 write 方式致使的內存拷貝問題
Netty是如何解決JDK中的Selector BUG
Selector BUG:若Selector的輪詢結果爲空,也沒有wakeup或新消息處理,則發生空輪詢,CPU使用率100%,安全
Netty的解決辦法:對Selector的select操做週期進行統計,每完成一次空的select操做進行一次計數,若在某個週期內連續發生N次空輪詢,則觸發了epoll死循環bug。重建Selector,判斷是不是其餘線程發起的重建請求,若不是則將原SocketChannel從舊的Selector上去除註冊,從新註冊到新的Selector上,並將原來的Selector關閉。
Netty的優點有哪些
- 使用簡單:封裝了 NIO 的不少細節,使用更簡單。
- 功能強大:預置了多種編解碼功能,支持多種主流協議。
- 定製能力強:能夠經過
ChannelHandler
對通訊框架進行靈活地擴展。
- 性能高:經過與其餘業界主流的 NIO 框架對比,Netty 的綜合性能最優。
- 穩定:Netty 修復了已經發現的全部 NIO 的 bug,讓開發人員能夠專一於業務自己。
- 社區活躍:Netty 是活躍的開源項目,版本迭代週期短,bug 修復速度快。
Netty 高性能表如今哪些方面
- IO 線程模型:同步非阻塞,用最少的資源作更多的事。
- 內存零拷貝:儘可能減小沒必要要的內存拷貝,實現了更高效率的傳輸。
- 內存池設計:申請的內存能夠重用,主要指直接內存。內部實現是用一顆二叉查找樹管理內存分配狀況。
- 串形化處理讀寫:避免使用鎖帶來的性能開銷。即消息的處理儘量在同一個線程內完成,期間不進行線程切換,這樣就避免了多線程競爭和同步鎖。表面上看,串行化設計彷佛CPU利用率不高,併發程度不夠。可是,經過調整NIO線程池的線程參數,能夠同時啓動多個串行化的線程並行運行,這種局部無鎖化的串行線程設計相比一個隊列-多個工做線程模型性能更優。
- 高性能序列化協議:支持
protobuf
等高性能序列化協議。
- 高效併發編程的體現:
volatile
的大量、正確使用;CAS
和原子類的普遍使用;線程安全容器的使用;經過讀寫鎖提高併發性能。
Netty中有哪些重要組件
- Channel:Netty 網絡操做抽象類,它除了包括基本的 I/O 操做,如
bind、connect、read、write
等。
- EventLoop:主要是配合 Channel 處理 I/O 操做,用來處理鏈接的生命週期中所發生的事情。
- ChannelFuture:Netty 框架中全部的 I/O 操做都爲異步的,所以咱們須要
ChannelFuture
的 addListener()
註冊一個 ChannelFutureListener
監聽事件,當操做執行成功或者失敗時,監聽就會自動觸發返回結果。
- ChannelHandler:充當了全部處理入站和出站數據的邏輯容器。
ChannelHandler
主要用來處理各類事件,這裏的事件很普遍,好比能夠是鏈接、數據接收、異常、數據轉換等。
- ChannelPipeline:爲
ChannelHandler
鏈提供了容器,當 channel 建立時,就會被自動分配到它專屬的 ChannelPipeline
,這個關聯是永久性的。
Netty 發送消息有幾種方式
Netty有兩種發送消息的方式網絡
- 直接寫入 Channel 中,消息從 ChannelPipeline 當中尾部開始移動;
- 寫入和 ChannelHandler 綁定的 ChannelHandlerContext 中,消息從 ChannelPipeline 中的下一個 ChannelHandler 中移動。
Netty的內存管理機制是什麼
- 首先會預申請一大塊內存
Arena
,Arena
由許多Chunk
組成,而每一個Chunk
默認由2048個page組成
-
Chunk
經過AVL樹的形式組織Page,每一個葉子節點表示一個Page,而中間節點表示內存區域,節點本身記錄它在整個Arena
中的偏移地址
- 當區域被分配出去後,中間節點上的標記位會被標記,這樣就表示這個中間節點如下的全部節點都已被分配了
- 大於8k的內存分配在
poolChunkList
中,而PoolSubpage
用於分配小於8k的內存,它會把一個page分割成多段,進行內存分配
ByteBuf的特色
- 支持自動擴容(4M),保證put方法不會拋出異常、經過內置的複合緩衝類型,實現零拷貝(zero-copy)
- 不須要調用
flip()
來切換讀/寫模式,讀取和寫入索引分開
- 引用計數基於
AtomicIntegerFieldUpdater
用於內存回收
- PooledByteBuf採用二叉樹來實現一個內存池,集中管理內存的分配和釋放,不用每次使用都新建一個緩衝區對象。UnpooledHeapByteBuf每次都會新建一個緩衝區對象。