天天看點

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

Netty的線程模型

1.1主從的 Reactor多線程模型

服務端用于接受用戶端的連接配接不再是一個單獨的NIO線程,而是一個獨立的NIO線程池,Accertor接收到用戶端的TCP連接配接請求之後,将新建立的SocketChannel注冊到 I/O 線程池 netty使用的是這種。

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

服務端啟動的時候,建立了兩個NioEventLoop,它們是兩個獨立的Reactor線程池,一個用于接受TCP的連接配接,另一個用于處理I/O相關的讀寫操作,或者執行系統的Task,定時任務Task。

用于接受用戶端連接配接的線程池職責是:

  • 接受用戶端的連接配接,初始化Channel參數
  • 将鍊路狀态變更事件 通知給ChannelPipline

用于處理I/O操作的線程池職責如下:

  • 異步讀取通信對端的資料包,發送讀事件到ChannelPipline;
  • 異步發送消息到通信對端,調用ChannelPipline的消息發送接口;
  • 執行系統的Task;
  • 執行定時的Task,例如鍊路空閑檢測定時任務;

1.2 基于該線程模型的最佳實踐

  • 建立兩個NioEventLoopGroup 用于隔離Nio Acceptor和Nio I/O 線程;
  • 盡量不要再ChannelHandler 中啟動使用者線程(解碼後用于将POJO 消息派發到後端服務端除外);
  • 解碼要放在Nio的解碼Handler 中進行,不要切換到使用者線程中解碼;
  • 如果業務邏輯非常簡單,沒有導緻線程阻塞的磁盤操作,資料庫操作,網絡操作,可以在NIO線程完成業務處理;
  • 如果業務邏輯比較複雜,不要再NIO線程完成,建議将解碼後的pojo消息封裝為task,派發到業務線程池中有業務線程處理,保證Nio線程盡快被釋放,處理其他的I/O操作。

1.2.1 推薦的線程數量計算公式

  • 線程數量 = (線程總時間 / 瓶頸資源時間)* 瓶頸資源的線程并行數

NioEventLoop的源碼分析

2.1 NioEventLoop的設計原理

NioEventLoop 并不是一個純粹的I/O線程 ,除了負責I/O操作,還兼顧處理一下任務:

  • 系統Task:通過調用execute(runable)方法實作,Netty有很多的系統task,建立他們的主要原因是:當I/O操作線程和使用者線程同時操作網絡資源時,為了防止并發導緻鎖競争,将使用者的線程的操作封裝為task 放入消息對列中,讓I/O線程去處理,這樣實作了局部無鎖。
  • 定時任務:調用schedule(runable,delay,timeunit)來實作。

繼承類圖

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

NioEventLoop

2.2run方法
Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析
Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

2.3 出現bug的原因

服務端等待連接配接,用戶端發起連接配接,發送消息,服務端接受連接配接,并注冊監聽通道的OP_READ,服務端讀取消息,從感興趣事件集合中移除OP_READ,用戶端關閉連接配接,服務端給用戶端發送消息,服務端select方法不再阻塞,無限喚醒并且傳回值為0.

2.4 輪循到就緒狀态SocketChannel 處理I/O

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析
Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析

processSelectedKey 方法分析

Netty EventLoop 和 EventLoopGroupNetty的線程模型NioEventLoop的源碼分析