亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

首頁 慕課教程 Netty 教程 Netty 教程 Netty ChannelHandler生命周期

Netty ChannelHandler 生命周期

1. 前言

本節內容,我們主要講解 ChannelHandler 在執行過程中的生命周期是什么樣的?需要執行哪些核心的生命周期方法以及順序?

了解生命周期的核心目的是,可以在合適的生命周期方法擴展自己的業務功能。

2. UML 關系

首先,我們先來了解以下 ChannelHandler 的類依賴關系圖,具體如下所示:
圖片描述

通過上面的類結構圖,我們總結一下規律:

  1. ChannelHandler 有兩個子接口,分別是 ChannelInboundHandlerChannelOutboundHandler,其實從字面意思就能知道,它們分別是入站和出站的接口類。
  2. 如果我們自定義的業務 Handler 直接實現 ChannelInboundHandler 或者 ChannelOutboundHandler,那么我們需要實現的接口非常的多,增加了開發的難度。Netty 已經幫我們封裝好了兩個實現類,分別是 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter,這樣可以大大簡化了開發工作。

3. 核心生命周期方法

方法 描述
handlerAdded Handler 被加入 Pipeline 時觸發(僅僅觸發一次)
channelRegistered channelRegistered 注冊成功時觸發
channelActive channel 連接就緒時觸發
channelRead channel 有數據可讀時觸發
channelReadComplete channel 有數據可讀,并且讀完時觸發
channelInactive channel 斷開時觸發
channelUnregistered channel 取消注冊時觸發
handlerRemoved handler 被從 Pipeline 移除時觸發

問題 1:channelRegistered 注冊指的是什么呢?

Channel 在創建時,需要綁定 ChannelPipeline 和 EventLoop 等操作,完成這些操作時會觸發 channelRegistered () 方法。

問題 2:channelRead 和 channelReadComplete 的區別?

當 Channel 有數據可讀時,會觸發 channelRead 事件,eventLoop 被喚醒并且調用 channelRead () 處理數據;eventLoop 喚醒后讀取數據包裝成 msg,然后將 msg 作為參數調用 channelRead (),期間做了個判斷,讀取到 0 字節或者讀取到的字節數小于 buffer 的容量,滿足以上條件就會調用 channelReadComplete ()。

4. 生命周期執行流程

ChannelHandler 的一些特殊回調方法,這些回調方法的執行是有順序的,而這個執行順序可以稱為 ChannelHandler 的生命周期。

4.1 客戶端發送一次請求

public class InboundHandler1 extends ChannelInboundHandlerAdapter {
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
        super.handlerAdded(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead");
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete");
        super.channelReadComplete(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
        super.channelInactive(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved");
        super.handlerRemoved(ctx);
    }
}

執行結果:

handlerAdded
channelRegistered
channelActive
channelRead
channelReadComplete

4.2 客戶端發送多次請求

客戶端每隔 5 秒鐘發送一次消息給服務端,查看效果如何?

實例:

final ChannelFuture channelFuture=bootstrap.connect("127.0.0.1",80).sync();
channelFuture.addListener(new ChannelFutureListener() {
    public void operationComplete(ChannelFuture future) throws Exception {
        if(future.isDone()){
            if(future.isSuccess()){
                
                //1秒鐘之后,每隔5描述發送一次消息
                channelFuture.channel().eventLoop().scheduleWithFixedDelay(new Runnable() {
                    public void run() {
                        channelFuture.channel().writeAndFlush("hello world");
                    }
                },1,5, TimeUnit.SECONDS);

            }else if(future.isCancelled()){
                System.out.println("連接被取消");
            }else if(future.cause()!=null){
                System.out.println("連接出錯:");
            }
        }
    }
});

執行結果:

handlerAdded
channelRegistered
channelActive
channelRead
channelReadComplete
    
channelRead
channelReadComplete
    
channelRead
channelReadComplete

通過執行結果我們發現,第一次的時候執行 handlerAdded ()、channelRegistered ()、channelActive (),后面就不會被執行了。

客戶端的每次請求時,都會觸發 channelRead () 和 channelReadComplete () 兩個核心方法。

4.3 手工關閉通道

疑問:channelInactive、channelUnregistered、handlerRemoved 什么時候會被執行呢?

實例:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("channelRead");
    //手工關閉通道
    ctx.channel().close();
}

執行結果:

handlerAdded
channelRegistered
channelActive
channelRead
channelReadComplete
channelInactive
channelUnregistered
handlerRemoved

總結,人為的關閉通道或者其他因素(比如:網絡故障等),則會觸發 channelInactive、channelUnregistered、handlerRemoved 的執行。

4.4 生命周期總結

我們來逐個總結一下每個回調方法的含義

  1. handlerAdded () :當檢測到新連接之后,調用 ch.pipeline().addLast(new LifeCycleHandler()); 之后的回調,表示在當前的 channel 中,已經成功添加了一個 handler 到雙向鏈表。
  2. channelRegistered ():這個回調方法,表示當前的 channel 的所有的邏輯處理已經和某個 NIO 線程建立了綁定關系,從線程池里面去抓一個線程綁定在這個 channel 上,這里的 NIO 線程通常指的是 NioEventLoop。
  3. channelActive ():當 channel 的所有的業務邏輯鏈準備完畢,channel 的 pipeline 中已經添加完所有的 handler 以及綁定好一個 NIO 線程之后,這條連接算是真正激活了,接下來就會回調到此方法。
  4. channelRead ():客戶端向服務端發來數據,每次都會回調此方法,表示有數據可讀。
  5. channelReadComplete ():服務端每次讀完一次完整的數據之后,回調該方法,表示數據讀取完畢。
  6. channelInactive (): 表面這條連接已經被關閉了,這條連接在 TCP 層面已經不再是 ESTABLISH 狀態了。
  7. channelUnregistered (): 既然連接已經被關閉,那么與這條連接綁定的線程就不需要對這條連接負責了,這個回調就表明與這條連接對應的 NIO 線程移除掉對這條連接的處理。
  8. handlerRemoved ():給這條連接上添加的所有的業務邏輯處理器都給移除掉。

ChannelHandler 回調方法的執行順序為

  1. 連接請求,handlerAdded () -> channelRegistered () -> channelActive () -> channelRead () -> channelReadComplete ();
  2. 數據請求,channelRead () -> channelReadComplete ();
  3. 通道被關閉,channelInactive () -> channelUnregistered () -> handlerRemoved ()。

圖片描述

5. 小結

本節內容主要講解 ChannelHandler 的生命周期方法的執行順序及觸發機制,目的是了解每個方法的觸發時間點,有助于業務點的擴展。核心掌握以下知識點:

  1. 核心的生命周期方法有哪些,它們的觸發時間點是什么;
  2. channelRegistered 需要清楚,這個不容易理解;
  3. channelRead 和 channelReadComplete 的區別,需要清楚;
  4. 通過三種 Demo 來說明了不同的生命周期方法的執行次數,有的是只執行一次,有的是每次都會執行。