Netty源碼分析(二)服務端 Channel 初始化原理

1. 嘮叨

及時分享學習所得。希望與您共勉,一起學習共同進步。

2. 服務端 Channel 初始化

(重點)我們還是先抓重點。下面是初始化 Netty 服務端 Channel 的主線,請牢記。

1. ServerBootstrap.bind(int inetPort)
2. AbstractBootstrap.bind(SocketAddress localAddress)
3. AbstractBootstrap.doBind(final SocketAddress localAddress)
4. AbstractBootstrap.initAndRegister()
5. ServerBootstrap.init(Channel channel)

前 4 步,與上一講服務端 Channel 創建原理一致。所以,初始化流程都在 ServerBootstrap.init 中

2.1 一起看源碼

(嘮叨)又到了有趣的查看源碼環節。現在打開您的 IDE 我們一起走一遍上面的流程。我們看到 ServerBootstrap.init 中大部分邏輯都是在初始化與創建對象。嗑...嗑...嗑...對本章就在學習初始化原理。相當正確沒跑題。

(重點)那初始化了什么?又創建了什么?

  1. 初始化 NioServerSocketChannel 的 config 與 attributes
  2. 在 pipeline 中添加 config.handler()
  3. 創建 ServerBootstrapAcceptor(連接器)并添加到 pipeline 中

源碼 1

// 我是 ServerBootstrap 類的 init 方法源碼
void init(Channel channel) throws Exception {
        // 初始化 NioServerSocketChannel 的 config 與 attributes
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            channel.config().setOptions(options);   // 1
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());  // 2
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size())); 
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size())); 
        }

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();  // 3
                if (handler != null) {
                    pipeline.addLast(handler);  // 在 pipeline 中添加 config.handler()
                }

                // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
                // In this case the initChannel(...) method will only be called after this method returns. Because
                // of this we need to ensure we add our handler in a delayed fashion so all the users handler are
                // placed in front of the ServerBootstrapAcceptor.
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        // 創建 ServerBootstrapAcceptor(連接器)并添加到 pipeline 中
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  
                    }
                });
            }
        });
    }

2.2 出師了

恭喜您,您已經掌握了服務端 Channel 的初始化原理。這次時候是真的,源碼也確實很簡單。是不是沒什么疑問了?真的沒有了?那么請嘗試回答這幾個問題 channel.config() 是什么?channel.attr 是什么?ChannelHandler handler 是什么?ServerBootstrapAcceptor 中的 4 個參數又什么是呢?(我又來賣關子了,你能怎樣?)如果以上問題您都清楚,那么恭喜您真的懂了。否則,我們繼續研究。

2.3 channel.config() 是什么

其實我上節講過,有圖有真相


圖 1

所以 NioServerSocketChannel 的配置保存在 ServerSocketChannelConfig 的實例對象 config 中,它是在 NioServerSocketChannel 的構造函數中創建的。

2.4 channel.attr 是什么

如果你已經打開了 IDE 此時你會發現它是 DefaultAttributeMap 中的方法。
(嘮叨)慌了東西越來越多,每次看開源代碼都是這樣,調用層次越來越深。最后直到迷失、放棄。
不急,下面我幫您分析一下。

1. NioServerSocketChannel extends AbstractNioMessageChannel
2. AbstractNioMessageChannel extends AbstractNioChannel
3. AbstractNioChannel extends AbstractChannel
4. AbstractChannel extends DefaultAttributeMap

怎么樣?懂了嗎?DefaultAttributeMap 是 NioServerSocketChannel 的爸爸。那它的 attr 自然也就被孩子繼承了。它里面存儲我們自定義的一些屬性(key,value)。

2.5 ChannelHandler handler 是什么

它是通過 AbstractBootstrapConfig 的 handler 方法獲取的

    public final ChannelHandler handler() {
        return bootstrap.handler();
    }

而 bootstrap.handler() 調用了 AbstractBootstrap 的 handler 方法,最終直接返回 handler

    final ChannelHandler handler() {
        return handler;
    }

現在我又需要上圖了

圖 2

由圖可知,它就是在第一節,源碼 1 標注 6 處我們傳入的 ServerHandler (這是一個自定義的 handler)。

2.6 ServerBootstrapAcceptor 中的 4 個參數是什么

currentChildGroup 就是 childGroup。 而 childGroup 需要參看 圖 2 源碼標準 2 處,我們調用了 ServerBootstrap 的 group(EventLoopGroup parentGroup, EventLoopGroup childGroup) 方法。所以,childGroup 就是我們傳入的 workerGroup

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        // 省略
        this.childGroup = childGroup;
        return this;
    }

currentChildHandler 就是 childHandler。而 childHandler 需要參看 圖 2 源碼標準 7 處,我們調用了 ServerBootstrap 的 childHandler(ChannelHandler childHandler) 方法。所以,childHandler 就是我們傳入的 ChannelInitializer
同理 currentChildOptions 和 currentChildAttrs 也很容易查找到,這里留給您自行分析。
通過以上分析,相信您已經掌握了服務端 Channel 的初始化原理。
最后,感謝您的閱讀,希望以上內容對您有所幫助。如果有疑問,歡迎留言與我討論。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容