Netty 源碼解析(六): Channel 的 register 操作


原創申明:本文由公眾號【猿燈塔】原創,轉載請說明出處標注


今天是猿燈塔“365篇原創計划”第六篇。
接下來的時間燈塔君持續更新Netty系列一共九篇
 
Netty 源碼解析(一 ):開始
Netty 源碼解析(二): Netty 的 Channel
Netty 源碼解析(三): Netty 的 Future 和 Promise
Netty 源碼解析(四): Netty 的 ChannelPipeline
Netty 源碼解析(五): Netty 的線程池分析
當前:Netty 源碼解析(六): Channel 的 register 操作
Netty 源碼解析(七): NioEventLoop 工作流程
Netty 源碼解析(八): 回到 Channel 的 register 操作
Netty 源碼解析(九): connect 過程和 bind 過程分析
今天呢!燈塔君跟大家講:
Channel 的 register 操作

Channel 的 register 操作

接下來,我們來分析 Netty 中的線程池。Netty 中的線程池比較不好理解,因為它的類比較多,而且它們之間的關系錯綜復雜。看下圖,感受下 NioEventLoop 類和 NioEventLoopGroup 類的繼承結構:經過前面的鋪墊,我們已經具備一定的基礎了,我們開始來把前面學到的內容揉在一起。這節,我們會介紹 register 操作,這一步其實是非常關鍵的,對於我們源碼分析非常重要。我們從 EchoClient 中的 connect() 方法出發,或者 EchoServer 的 bind(port) 方法出發,都會走到 initAndRegister() 這個方法:
 final ChannelFuture initAndRegister() {
     Channel channel = null;
     try {
         // 1
         channel = channelFactory.newChannel();
         // 2 對於 Bootstrap 和 ServerBootstrap,這里面有些不一樣
         init(channel);
     } catch (Throwable t) {
         ...
    }
    // 3 我們這里要說的是這行
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}
initAndRegister() 這個方法我們已經接觸過兩次了,前面介紹了 1️⃣ Channel 的實例化,實例化過程中,會執行 Channel 內部 Unsafe 和 Pipeline 的實例化,以及在上面 2️⃣ init(channel) 方法中,會往 pipeline 中添加 handler(pipeline 此時是 head+channelnitializer+tail)。
我們這節終於要揭秘 ChannelInitializer 中的 initChannel 方法了~~~
現在,我們繼續往下走,看看 3️⃣ register 這一步:
ChannelFuture regFuture = config().group().register(channel);

我們說了,register 這一步是非常關鍵的,它發生在 channel 實例化以后,大家回憶一下當前 channel 中的一些情況:實例化了 JDK 底層的 Channel,設置了非阻塞,實例化了 Unsafe,實例化了 Pipeline,同時往 pipeline 中添加了 head、tail 以及一個 ChannelInitializer 實例。
上面的 config().group() 方法會返回前面實例化的 NioEventLoopGroup 的實例,然后調用其 register(channel) 方法:// MultithreadEventLoopGroup
1@Override
2public ChannelFuture register(Channel channel) {
3    return next().register(channel);
4}
next() 方法很簡單,就是選擇線程池中的一個線程(還記得 chooserFactory 嗎),也就是選擇一個 NioEventLoop 實例,這個時候我們就進入到 NioEventLoop 了。NioEventLoop 的 register(channel) 方法實現在它的父類 SingleThreadEventLoop 中:
@Override
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}
上面的代碼實例化了一個 Promise,將當前 channel 帶了進去:
@Override
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    // promise 關聯了 channel,channel 持有 Unsafe 實例,register 操作就封裝在 Unsafe 中
    promise.channel().unsafe().register(this, promise);
    return promise;
}
拿到 channel 中關聯的 Unsafe 實例,然后調用它的 register 方法:
我們說過,Unsafe 專門用來封裝底層實現,當然這里也沒那么“底層”
// AbstractChannel#AbstractUnsafe
 @Override
 public final void register(EventLoop eventLoop, final ChannelPromise promise) {
     ...
     // 將這個 eventLoop 實例設置給這個 channel,從此這個 channel 就是有 eventLoop 的了
     // 我覺得這一步其實挺關鍵的,因為后續該 channel 中的所有異步操作,都要提交給這個 eventLoop 來執行
     AbstractChannel.this.eventLoop = eventLoop;

     // 如果發起 register 動作的線程就是 eventLoop 實例中的線程,那么直接調用 register0(promise)
     // 對於我們來說,它不會進入到這個分支,
    //     之所以有這個分支,是因為我們是可以 unregister,然后再 register 的,后面再仔細看
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            // 否則,提交任務給 eventLoop,eventLoop 中的線程會負責調用 register0(promise)
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            ...
        }
    }


到這里,我們要明白,NioEventLoop 中是還沒有實例化 Thread 實例的。
這幾步涉及到了好幾個類:NioEventLoop、Promise、Channel、Unsafe 等,大家要仔細理清楚它們的關系。對於我們前面過來的 register 操作,其實提交到 eventLoop 以后,就直接返回 promise 實例了,剩下的register0 是異步操作,它由 NioEventLoop 實例來完成。我們這邊先不繼續往里分析 register0(promise) 方法,先把前面欠下的 NioEventLoop 中的線程介紹清楚,然后再回來介紹這個 register0 方法。
Channel 實例一旦 register 到了 NioEventLoopGroup 實例中的某個 NioEventLoop 實例,那么后續該 Channel 的所有操作,都是由該 NioEventLoop 實例來完成的。這個也非常簡單,因為 Selector 實例是在 NioEventLoop 實例中的,Channel 實例一旦注冊到某個 Selector 實例中,當然也只能在這個實例中處理 NIO 事件。

本章節就到這了,敬請期待!

 

365天干貨不斷微信搜索「猿燈塔」第一時間閱讀,回復【資料】【面試】【簡歷】有我准備的一線大廠面試資料和簡歷模板


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM