Netty IO隨記

Netty全部是異步IO操作,所有操作的是立即返回。可以通過future/監聽器,等待/監聽異步回調的結果。

注意:Netty的IO線程(boss+worker)最好只做不費時間的CPU操作,避免IO阻塞影響吞吐量。複雜業務請提交到自定義的業務線程池隊列中,再由業務線程通過channel響應客戶端請求。

線程模型

Netty Reactor 主從模型,介紹服務端 Netty 的工作架構圖:

角色

  • 一個EventLoop有一個Thread線程對象,持有一個Selector(NIO多路複用器,調用系統內核select方法)。線程代碼一直循環監聽Selector。
  • 一個EventLoopGroup相當於一個EventLoop池。
  • 每一個Socket連接封裝到一個SocketChannel中,是接收、發送信息的管道。
  • BossEventLoopGroup持有多個EventLoop,其Selector只負責監聽Socket-Accetpt事件,將接入的Socket初始化SocketChannel,生成ChannelPipeline處理器鏈。最後註冊到Work-Selector中。
  • WorkEventLoopGroup與Boss同理,但Selector只負責監聽Socket-讀寫事件。(JavaNio模型中通過Selector多路複用器掃描指定事件Key監聽觸發事件的Channel)。
  • Socket讀寫事件經過SocketChannel的ChannelPipeline鏈中的In/OutChannelHandler入/出站處理器。
  • ChannelPipeline和ChannelHandler默認是Channel私有的。爲ChannelHandler增加@Sharable註解使其變成單例。
  • TaskQueue、ScheduleTaskQueue用於單線程異步執行ChannelHandler中費時間的操作,避免EventLoop被阻塞。在ChannelHandler中執行ctx.channel().eventLoop().execute(new Runnable())可以將Runnable投遞到其TaskQueue。

 

server流程圖注意圖中 NioEventGroup 是 NioEventLoop

1)初始化創建 2 個 NioEventLoopGroup:其中 boosGroup 用於 Accetpt 連接建立事件並分發請求,workerGroup 用於監聽處理 I/O 讀寫事件和業務邏輯。

2)基於 ServerBootstrap(服務端啓動引導類):配置 EventLoopGroup、Channel 類型,連接參數、配置入站、出站事件 handler。

3)綁定端口:開始工作。

Server 端包含 1 個 Boss NioEventLoopGroup 和 1 個 Worker NioEventLoopGroup。

NioEventLoopGroup 相當於 1 個事件循環組,這個組裏包含多個事件循環 NioEventLoop,每個 NioEventLoop 包含 1 個 Selector 和 1 個事件循環線程。

每個 Boss NioEventLoop 循環執行的任務包含 3 步:

1)輪詢 Accept 事件;

2)處理 Accept I/O 事件,與 Client 建立連接,生成 NioSocketChannel,並將 NioSocketChannel 註冊到某個 Worker NioEventLoop 的 Selector 上;

3)處理任務隊列runAllTasks中的任務。任務隊列中的任務包括用戶調用 eventloop.execute 或 schedule 執行的任務,或者其他線程提交到該 eventloop 的任務。

每個 Worker NioEventLoop 循環執行的任務包含 3 步:

1)輪詢 Read、Write 事件;

2)處理 I/O 事件,即 Read、Write 事件,在 NioSocketChannel 可讀、可寫事件發生時進行處理;

3)處理任務隊列中的任務,runAllTasks。

其中任務隊列中的 Task 有 3 種典型使用場景:

① 用戶程序自定義的普通任務:

ctx.channel().eventLoop().execute(newRunnable() {

   @Override

   publicvoidrun() {

       //...

   }

});

② 非當前 Reactor 線程調用 Channel 的各種方法:

例如在推送系統的業務線程裏面,根據用戶的標識,找到對應的 Channel 引用,然後調用 Write 類方法向該用戶推送消息,就會進入到這種場景。最終的 Write 會提交到任務隊列中後被異步消費。

③ 用戶自定義定時任務:

ctx.channel().eventLoop().schedule(newRunnable() {

   @Override

   publicvoidrun() {

 

   }

}, 60, TimeUnit.SECONDS);

線程切換 

通過Queue實現Netty IO線程與業務線程的切換,最後的ChannelHandler將msg和channel等封裝成Runnable送入業務線程池Queue。業務線程從Queue中拉取信息處理。業務線程通過channel向客戶端發送信息,會調用ChannelPipeline的出站流程。

從上圖可以看出,Outbound操作的主要處理流程如下:

  • I/O線程NioEventLoop從SocketChannel中讀取數據報,將ByteBuf投遞到ChannelPipeline,觸發ChannelRead事件;
  • I/O線程NioEventLoop調用ChannelHandler鏈,直到將消息投遞到自定義業務線程池中執行Runnable,然後I/O線程返回,繼續後續的讀寫操作;
  • 業務線程調用ChannelHandlerContext.write(Object msg)方法進行消息發送;
  • 如果是由業務線程發起的寫操作,ChannelHandlerInvoker將發送消息封裝成Task,放入到I/O線程NioEventLoop的任務隊列中,由NioEventLoop在循環中統一調度和執行。放入任務隊列之後,業務線程返回;
  • I/O線程NioEventLoop調用ChannelHandler鏈,進行消息發送,處理Outbound事件,直到將消息放入發送隊列,然後喚醒Selector,進而執行寫操作。

 

心跳檢測 IdleStateHandler

/p/10ee5629d580

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章