Netty學習之核心組件(AbstractBootstrap、Bootstrap、ServerBootstrap)


  Netty的核心組件的設計都很模塊化,如果想要實現一個應用程序,就需要將這些組件組裝到一起。Netty通過Bootstrap類,以對一個Netty應用程序進行配置(組裝各個組件),並最終使它運行起來。對於客戶端程序和服務器程序所使用到的Bootstrap類是不同的,后者需要使用ServerBootstrap,這樣設計是因為,在如TCP這樣有連接的協議中,服務器程序往往需要一個以上的Channel,通過父Channel來接受來自客戶端的連接,然后創建子Channel用於它們之間的通信,而像UDP這樣無連接的協議,它不需要每個連接都創建子Channel,只需要一個Channel即可。

  一個比較明顯的差異就是Bootstrap與ServerBootstrap的group()方法,后者提供了一個接收2個EventLoopGroup的版本。

 

Bootstrap

ServerBootstrap

網絡編程中的作用

連接到遠程主機和端口

綁定到一個本地端口

EventLoopGroup 的數目 

1

2

一、AbstractBootstrap類

  AbstractBootstrap是一個工具類,用於服務器通道的一系列配置,綁定NioEventLoopGroup線程組、指定NIO的模式、指定子處理器,用於處理workerGroup、指定端口等。其有ServerBootstrap、Bootstrap兩個具體的實現。

  總的來說:

1、提供了一個ChannelFactory對象用來創建Channel,一個Channel會對應一個EventLoop用於IO的事件處理,在一個Channel的整個生命周期中
  只會綁定一個EventLoop,這里可理解給Channel分配一個線程進行IO事件處理,結束后回收該線程。
2、AbstractBootstrap沒有提供EventLoop而是提供了一個EventLoopGroup,上篇博客講過EventLoopGroup對象就是一個含有EventLoop的數組。 但是當一個連接到達,Netty會注冊一個Channel,然后EventLoopGroup會分配一個EventLoop綁定到這個channel。 3、不管是服務器還是客戶端的Channel都需要綁定一個本地端口這就有了SocketAddress類的對象localAddress。 4、Channel有很多選項所有有了options對象LinkedHashMap<channeloption<?>, Object> 5、怎么處理Channel的IO事件呢,我們添加一個事件處理器ChannelHandler對象。

  關於AbstractBootstrap類,其代碼如下:

public abstract class AbstractBootstrap<B extends io.netty.bootstrap.AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    /**
     * 這里的 EventLoopGroup 作為服務端 Acceptor 線程,負責處理客戶端的請求接入
     * 作為客戶端 Connector 線程,負責注冊監聽連接操作位,用於判斷異步連接結果。
     */
    volatile EventLoopGroup group;

    /**
     * 創建Channer 工廠 根據傳入的類型來創建不同的Channel 
     * 比如服務器傳入的是:NioServerSocketChannel.class
     * 客戶端傳入:NioSocketChannel.class 。 加上這個注解代表這個已經過期有更好的替代類
     */
    @SuppressWarnings("deprecation")
    private volatile ChannelFactory<? extends C> channelFactory;

    /**
     * SocketAddress 是用來綁定一個服務端口 用的
     */
    private volatile SocketAddress localAddress;

    /**
     * ChannelOption 可以添加Channel 添加一些配置信息
     */
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();

    /**
     * ChannelHandler 是具體怎么處理Channel 的IO事件。
     */
    private volatile ChannelHandler handler;

    /**
     * 傳入一個EventLoopGroup,不管服務端還是客戶端都會調用該方法
     */
    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return self();
    }

    /**
     * 返回對象本身
     */
    @SuppressWarnings("unchecked")
    private B self() {
        return (B) this;
    }

    /**
     *設置服務端的Channel,Netty通過Channel工廠類創建不同的Channel。
     * 對於服務端傳入:Netty需要創建NioServerSocketChannel
     * 對於客戶端傳入:NioSocketChannel.class
     */
    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

    /**
     * 創建好Channel后,返回對象本身
     */
    @Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return self();
    }

    /**
     * 設置一些Channel相關參數
     */
    public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
                options.put(option, value);
            }
        }
        return self();
    }

    /**
     *  服務端方法: 綁定端口 對該端口進行監聽
     */
    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

    /**
     * 客戶端方法: 需要傳入訪問的地址和端口
     */
    public ChannelFuture bind(String inetHost, int inetPort) {
        return bind(SocketUtils.socketAddress(inetHost, inetPort));
    }

    public ChannelFuture bind(SocketAddress localAddress) {

        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        //這個方法這里省略調,具體可以看源碼
        return doBind(localAddress);
    }

    /**
     * 設置父類的Handler,父類的handler是客戶端新接入的接連SocketChannel對應的ChannelPipeline 的handler
     */
    public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return self();
    }
}

二、Bootstrap類

  Bootstrap 是 Netty 提供的一個便利的工廠類, 我們可以通過它來完成 Netty 的客戶端或服務器端的 Netty 初始化。Bootstrap: 用於客戶端,只需要一個單獨的Channel,來與服務端進行數據交互,對應server端的子Channel。

  作用職責:EventLoop初始化, channel的注冊過程, 關於pipeline的初始化, handler的添加過程, 客戶端連接分析。

  如Demo中代碼所示:

EventLoopGroup worker = new NioEventLoopGroup();
        try {
            // 客戶端啟動類程序
            Bootstrap bootstrap = new Bootstrap();
            /**
             *EventLoop的組
             */
            bootstrap.group(worker);
            /**
             * 用於構造socketchannel工廠
             */
            bootstrap.channel(NioSocketChannel.class);
            /**設置選項
             * 參數:Socket的標准參數(key,value),可自行百度
             保持呼吸,不要斷氣!
             * */
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            /**
             * 自定義客戶端Handle(客戶端在這里搞事情)
             */
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new SimpleNettyClientHandler());
                }
            });

            /** 開啟客戶端監聽,連接到遠程節點,阻塞等待直到連接完成*/
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            /**阻塞等待數據,直到channel關閉(客戶端關閉)*/
            channelFuture.channel().closeFuture().sync();
        } finally {
            worker.shutdownGracefully();
        }

  從上面的客戶端代碼展示了 Netty 客戶端初始化時所需的所有內容:

1. EventLoopGroup: 不論是服務器端還是客戶端, 都必須指定 EventLoopGroup. 在這個例子中, 指定了 NioEventLoopGroup, 表示一個 NIO的EventLoopGroup. 2. ChannelType: 指定 Channel 的類型. 因為是客戶端, 因此使用了 NioSocketChannel. 3. Handler: 設置數據的處理器. 4. 這里的option,提供了一系列的TCP參數

  深入代碼, 看一下客戶端通過 Bootstrap 啟動后, 都做了哪些工作。

  1、group(group)

 /**
  * 直接調用父類AbstractBootstrap的方法
  */
public B group(EventLoopGroup group) {
    if (group == null) {
        throw new NullPointerException("group");
    }
    if (this.group != null) {
        throw new IllegalStateException("group set already");
    }
    this.group = group;
    return self();
}

  直接調用父類的方法 ,說明該EventLoopGroup,作為客戶端 Connector 線程,負責注冊監聽連接操作位,用於判斷異步連接結果。

  2、channel(NioServerSocketChannel.class)

  在 Netty 中, Channel是一個Socket的抽象, 它為用戶提供了關於 Socket 狀態(是否是連接還是斷開) 以及對 Socket 的讀寫等操作. 每當 Netty 建立了一個連接后, 都會有一個對應的 Channel 實例。

/**
 * 同樣也是直接調用父類AbstractBootstrap的方法
 */
    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

  ReflectiveChannelFactory類:

    public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

        private final Class<? extends T> clazz;

        /**
         * 通過構造函數 傳入 clazz
         */
        public ReflectiveChannelFactory(Class<? extends T> clazz) {
            if (clazz == null) {
                throw new NullPointerException("clazz");
            }
            this.clazz = clazz;
        }

        /**
         * 只用這一個方法 通過傳入不同的Channel.class 創建不同的Channel 對象。
         * newChannel() 什么時候調用呢 仔細追源碼 發現是在綁定 IP 和 端口的 doResolveAndConnect方法里會調用
         */
        @Override
        public T newChannel() {
            try {
                return clazz.getConstructor().newInstance();
            } catch (Throwable t) {
                throw new ChannelException("Unable to create Channel from class " + clazz, t);
            }
        }

  再看channelFactory(new ReflectiveChannelFactory(channelClass)) 方法:

   /**
     * 創建好Channel后,返回對象Bootstrap本身
     */
    @Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }
        this.channelFactory = channelFactory;
        return self();
    }

  因此對於我們這個例子中的客戶端的 Bootstrap 而言, 生成的的 Channel 實例就是 NioSocketChannel。

  Channel 類型

  除了 TCP 協議以外, Netty 還支持很多其他的連接協議, 並且每種協議還有 NIO(異步 IO) 和 OIO(Old-IO, 即傳統的阻塞 IO) 版本的區別. 不同協議不同的阻塞類型的連接都有不同的 Channel 類型與之對應,下面是一些常用的 Channel 類型:

- NioSocketChannel, 代表異步的客戶端 TCP Socket 連接. - NioServerSocketChannel, 異步的服務器端 TCP Socket 連接. - NioDatagramChannel, 異步的 UDP 連接 - NioSctpChannel, 異步的客戶端 Sctp 連接. - NioSctpServerChannel, 異步的 Sctp 服務器端連接. - OioSocketChannel, 同步的客戶端 TCP Socket 連接. - OioServerSocketChannel, 同步的服務器端 TCP Socket 連接. - OioDatagramChannel, 同步的 UDP 連接 - OioSctpChannel, 同步的 Sctp 服務器端連接. - OioSctpServerChannel, 同步的客戶端 TCP Socket 連接.

  3、handler(ChannelHandler handler)

  Netty 的一個強大和靈活之處就是基於 Pipeline 的自定義 handler 機制. 基於此, 我們可以像添加插件一樣自由組合各種各樣的 handler 來完成業務邏輯. 例如我們需要處理 HTTP 數據, 那么就可以在 pipeline 前添加一個 Http 的編解碼的 Handler, 然后接着添加我們自己的業務邏輯的 handler, 這樣網絡上的數據流就向通過一個管道一樣, 從不同的 handler 中流過並進行編解碼, 最終在到達我們自定義的 handler 中。

/**
 * 同樣也是 直接調用父類 AbstractBootstrap 的方法
 */
public B handler(ChannelHandler handler) {
    if (handler == null) {
        throw new NullPointerException("handler");
    }
    this.handler = handler;
    return self();
}

  常用寫法為使用ChannelInitializer進行channel配置:

.handler(new ChannelInitializer<SocketChannel>() {
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new EchoClientHandler());
        }
     })

  那是因為 Bootstrap.handler() 方法接收一個 ChannelHandler, 而我們傳遞的是一個 派生於 ChannelInitializer 的匿名類, 它正好也實現了 ChannelHandler 接口. 我們來看一下, ChannelInitializer 類部分代碼:

    /**
     * ChannelInboundHandlerAdapter 父類的父類 最終會繼承 ChannelHandler 
     * 那么ChannelInitializer 也就是 ChannelHandler的 子類
     */
    public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {

        private static final InternalLogger logger
            =InternalLoggerFactory.getInstance(ChannelInitializer.class);

        /**
         * 這里只有這一個抽象類 所以我們只需重寫這一個方法就可以了
         */
        protected abstract void initChannel(C ch) throws Exception;

        @Override
        @SuppressWarnings("unchecked")
        public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            initChannel((C) ctx.channel());
            ctx.pipeline().remove(this); // 移除自身
            ctx.fireChannelRegistered();
        }
   
    }

  ChannelInitializer 是一個抽象類, 它有一個抽象的方法 initChannel, 我們正是實現了這個方法, 並添加的自定義的 handler 的. 那么 initChannel 是哪里被調用的呢?

  答案是 ChannelInitializer.channelRegistered 方法中。

  我們來關注一下 channelRegistered 方法. 從上面的源碼中, 我們可以看到, 在 channelRegistered 方法中, 會調用 initChannel 方法, 將自定義的 handler 添加到 ChannelPipeline 中, 然后調用 ctx.pipeline().remove(this) 將自己從 ChannelPipeline 中刪除. 上面的分析過程, 可以用如下圖片展示:

  一開始, ChannelPipeline 中只有三個 handler:head, tail 和我們添加的 ChannelInitializer。

          

   接着 initChannel 方法調用后, 添加了自定義的 handler:

      

   最后將 ChannelInitializer 刪除,僅剩下head、tail以及自己添加的handler:

           

  4、ChannelPipeline對象

   /**
     * 我們在initChannel抽象方法的實現方法中 通過 SocketChannel獲得 ChannelPipeline對象
     */
    ChannelPipeline p = ch.pipeline();
    p.addLast(newEchoClientHandler());

  在實例化一個 Channel 時, 會伴隨着一個 ChannelPipeline 的實例化, 並且此 Channel 會與這個 ChannelPipeline 相互關聯, 這一點可以通過NioSocketChannel 的父類 AbstractChannel 的構造器:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    unsafe = newUnsafe();
    //這個可以看出
    pipeline = new DefaultChannelPipeline(this);
}

  當實例化一個 Channel(這里以 SimpleNettyClient 為例, 那么 Channel 就是 NioSocketChannel), 其 pipeline 字段就是我們新創建的 DefaultChannelPipeline 對象, 那么我們就來看一下 DefaultChannelPipeline 的構造方法。

public DefaultChannelPipeline(AbstractChannel channel) {
    if (channel == null) {
        throw new NullPointerException("channel");
    }
    this.channel = channel;

    tail = new TailContext(this);
    head = new HeadContext(this);

    head.next = tail;
    tail.prev = head;
}

  我們調用 DefaultChannelPipeline 的構造器, 傳入了一個 channel, 而這個 channel 其實就是我們實例化的 NioSocketChannel, DefaultChannelPipeline 會將這個 NioSocketChannel 對象保存在channel 字段中。DefaultChannelPipeline 中, 還有兩個特殊的字段, 即head 和tail, 而這兩個字段是一個雙向鏈表的頭和尾. 其實在 DefaultChannelPipeline 中, 維護了一個以 AbstractChannelHandlerContext 為節點的雙向鏈表, 這個鏈表是 Netty 實現 Pipeline 機制的關鍵。

  5、.connect(host, port)

  經過上面的各種分析后, 我們大致了解了 Netty 初始化時, 所做的工作, 接下來 分析一下客戶端是如何發起 TCP 連接的。

   /**
     * 1、 這里 終於是Bootstrap 自己的方法了。 傳入IP 地址 和 端口號
     */
    public ChannelFuture connect(String inetHost, int inetPort) {
        //通過InetSocketAddress 構造函數 綁定 IP地址+端口號
        return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
    }

   /**
     * 2、上面調用該方法 ,該方法在調用 doResolveAndConnect方法
     */
    public ChannelFuture connect(SocketAddress remoteAddress) {
        if (remoteAddress == null) {
            throw new NullPointerException("remoteAddress");
        }
        validate();
        return doResolveAndConnect(remoteAddress, config.localAddress());
    }

   /**
     * 3、這步 實例化 Channer
     */
    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
      
        //注意 這里 initAndRegister()方法就是實例化 Channer 的方法 上面說過 真正獲取Channer 對象 是在這步獲取的
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();

           // 這里省略的 很大一部分邏輯判斷的代碼
            return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());   
    }

    /**
     * 3.1 這里 就開始 調 newChannel() 方法 也就創建了 Channel 對象
     */
    final ChannelFuture initAndRegister() {
        Channel channel = null;
                channel = channelFactory.newChannel();
        return regFuture;
    }

    /**
     * 4、在看doResolveAndConnect0方法
     *    這一步還是對一些 參數數據 進行校驗  省略了校驗代碼
     */
    private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                               final SocketAddress localAddress, final ChannelPromise promise) {
           // 獲取 當前 EventLoop線程
            final EventLoop eventLoop = channel.eventLoop();
            final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
            final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
                   //這一步 才是 連接的關鍵
 doConnect(resolveFuture.getNow(), localAddress, promise);
           
        return promise;
    }

  在 connect 中, 會進行一些參數檢查后, 最終調用的是 doConnect 方法。

三、ServerBootstrap類

  ServerBootstrap可以理解為服務器啟動的工廠類,我們可以通過它來完成服務器端的 Netty 初始化。

  作用職責: EventLoop初始化channel的注冊過程 , 關於pipeline的初始化handler的添加過程, 服務端連接分析。

  來看以下Demo中的源碼:

        // 服務器端應用程序使用兩個NioEventLoopGroup創建兩個EventLoop的組,EventLoop這個相當於一個處理線程,是Netty接收請求和處理IO請求的線程。
        // 主線程組, 用於接受客戶端的連接,但是不做任何處理,跟老板一樣,不做事
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 從線程組, 當boss接受連接並注冊被接受的連接到worker時,處理被接受連接的流量。
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // netty服務器啟動類的創建, 輔助工具類,用於服務器通道的一系列配置
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            /**
             * 使用了多少線程以及如何將它們映射到創建的通道取決於EventLoopGroup實現,甚至可以通過構造函數進行配置。
             * 設置循環線程組,前者用於處理客戶端連接事件,后者用於處理網絡IO(server使用兩個參數這個)
             * public ServerBootstrap group(EventLoopGroup group)
             * public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)
             */
            serverBootstrap.group(bossGroup, workerGroup)           //綁定兩個線程組
                    // 用於構造socketchannel工廠
                    .channel(NioServerSocketChannel.class)   //指定NIO的模式
                    /**
                     * @Description: 初始化器,channel注冊后,會執行里面的相應的初始化方法,傳入自定義客戶端Handle(服務端在這里操作)
                     *
                     @Override
                     protected void initChannel(SocketChannel channel) throws Exception {
                     // 通過SocketChannel去獲得對應的管道
                     ChannelPipeline pipeline = channel.pipeline();

                     // 通過管道,添加handler
                     pipeline.addLast("nettyServerOutBoundHandler", new NettyServerOutBoundHandler());
                     pipeline.addLast("nettyServerHandler", new NettyServerHandler());
                     }
                      * 子處理器也可以通過下面的內部方法來實現。
                     */
                    .childHandler(new ChannelInitializer<SocketChannel>() {  // 子處理器,用於處理workerGroup
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new SimpleNettyServerHandler());

                        }
                    });

            // 啟動server,綁定端口,開始接收進來的連接,設置8088為啟動的端口號,同時啟動方式為同步
            ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

            System.out.println("server start");
            // 監聽關閉的channel,等待服務器 socket 關閉 。設置位同步方式
            channelFuture.channel().closeFuture().sync();
        } finally {
            //退出線程組
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

  1、group(bossGroup, workerGroup)

  跟客戶端明顯的一個區別就是,客戶端只傳入了一個NioEventLoopGroup, 而服務端傳入了兩個。看源碼:

/**
  * 這里調用的是 ServerBootstrap 類本身的 group 方法 發現傳入的兩個EventLoopGroup
  * 一個賦值給父類(AbstractBootstrap),另一個賦值給 該對象本身屬性
  */
 public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {

     //調用父類的group方法
     super.group(parentGroup);
     if (childGroup == null) {
         throw new NullPointerException("childGroup");
     }
     if (this.childGroup != null) {
         throw new IllegalStateException("childGroup set already");
     }
     this.childGroup = childGroup;
     return this;
 }

  在服務器端的初始化時, 我們設置一個是 bossGroup, 另一個是 workerGroup. 那么這兩個 EventLoopGroup 到底是怎么分工的?

  bossGroup 是用於服務端 的 accept 的, 即用於處理客戶端的連接請求。workerGroup 它們負責客戶端連接通道的 IO 操作。關於 bossGroup 與 workerGroup 的關系, 我們可以用如下圖來展示:

      

  首先, 服務器端 bossGroup 不斷地監聽是否有客戶端的連接, 當發現有一個新的客戶端連接到來時, bossGroup 就會為此連接初始化各項資源,然后從 workerGroup 中選出一個 EventLoop 綁定到此客戶端連接中. 那么接下來的服務器與客戶端的交互過程就全部在此分配的 EventLoop 中了。至於 bossGroup 和 workerGroup 和 channel 如何聯系到一起的,等下面再講bind(host)方法的時候在用源碼展示,因為是通過bind(host)開始將他們聯系到一起的。

  2、channel(NioServerSocketChannel.class)方法

  這里傳入的類是NioServerSocketChannel,而客戶端是NioSocketChannel,但他們都是通過類的反射機制獲得類的對象的。同樣真正用到該對象的時候,也是在bind(host)方法里。

  3、handler()和childHandler()

  跟客戶端比較發現還是有明顯區別的, 和 EventLoopGroup 一樣, 服務器端的 handler 也有兩個, 一個是通過 handler() 方法設置 handler 字段, 另一個是通過childHandler() 設置 childHandler 字段。不過handler()方法並不是必須的,而childHandler()方法是必須調用的。看代碼:

    /**handler(new LoggingHandler(LogLevel.INFO))
     * 
     * 我們發現channel方法調用的是父類(AbstractBootstrap)的方法
     * 所以這個 handler  字段與 accept 過程有關, 即這個 handler 負責處理客戶端的連接請求
     */
    public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return self();
    }

    /** 再看childHandler(class)
     * 
     *很明顯 這個childHandler 方法是屬於ServerBootstrap 本身的方法
     * 所以推測: 這個childHandler 就是負責和客戶端的連接的 IO 交互
     */
    public ServerBootstrap childHandler(ChannelHandler childHandler) {
        if (childHandler == null) {
            throw new NullPointerException("childHandler");
        }
        this.childHandler = childHandler;
        return this;
    }

  4、bind(host)方法

  bind(host)才是整個流程的關鍵,前面做得只是初始化了一些netty客戶端運行的對象(可以理解成只是創建了對象,並沒有使用它),但真正用到這些這些對象,還是在bind(host)方法里。如源碼:

        /**
         * 1、調用父類(AbstractBootstrap)的方法
         * <p>
         * 作用: 根據端口號 創建一個InetSocketAddress對象,用於連接連接服務器
         */
        public ChannelFuture bind(int inetPort) {
            return bind(new InetSocketAddress(inetPort));
        }

        /**
         * 2、繼續調用父類(AbstractBootstrap)的方法
         * <p>
         * 作用: 做一些校驗工作
         */
        public ChannelFuture bind(SocketAddress localAddress) {
            validate();
            if (localAddress == null) {
                throw new NullPointerException("localAddress");
            }
            return doBind(localAddress);
        }


        /**
         * 3、繼續調用父類(AbstractBootstrap)的方法
         * <p>
         * 作用: 這個方法做了很多事情
         */
        private ChannelFuture doBind(final SocketAddress localAddress) {
            //3、1 具體看下面3、1的代碼部分
            final ChannelFuture regFuture = initAndRegister();
            final Channel channel = regFuture.channel();

            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        }
    }

    /**
     * 3、1  這步做了很多重要的事情
     */
    final ChannelFuture initAndRegister() {
        Channel channel = null;

        //這里終於調用newChannel方法了,這里就是之前BootStrap講的ReflectiveChannelFactory對象的方法,這里的
        //channel 對象是NioServerSocketChannel。
        channel = channelFactory.newChannel();
        //這個方法也太重要了 和handle有關 下面3.1.1 講它
        init(channel);

        //這里的group()獲取的就是bootstrap ,這里面會調用next方法 來循環獲取下一個channel 具體的我就不點進去分析了
        //這里group().register(channel) 將 bossGroup 和 NioServerSocketChannel 關聯起來了.
        ChannelFuture regFuture = config().group().register(channel);

        return regFuture;
    }

    /**
     * 3.1.1 首先可以看到init的方法在父類(AbstractBootstrap)已經提供,只是子類寫具體實現代碼
     */
    abstract void init(Channel channel) throws Exception;

    /**
     * 我們再來看ServerBootstrap實現了init方法,這里面做了很多事
     * 比如workerGroup相關,還有handel相關
     */
    @Override
    void init(Channel channel) throws Exception {

        //通過channel獲得ChannelPipeline,說明每一個channel都會對應一個ChannelPipeline
        ChannelPipeline p = channel.pipeline();

        //這里終於獲得workerGroup 對象
        final EventLoopGroup currentChildGroup = childGroup;
        //這里獲得childHandler對象
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                //獲得handel方法傳入的對象
                ChannelHandler handler = config.handler();

                //這一步說明 .handler(new LoggingHandler(LogLevel.INFO))方法不是必須要的
                //如果你沒有調handler方法也沒有關系 ,因為它會在這路做一層判斷
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                //到這里線程就開始啟動運行了 發現已經講Channel,ChannelPipeline,workerGroup,childHandler等全部聯系到了一起。
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }


免責聲明!

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



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