• 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));
                        }
                    });
                }
            });
        }
  • 相关阅读:
    《转》 在C++中使用TinyXML2解析xml
    基于多种转换语义的图数据库查询
    tomcat内存、连接数优化
    JAVA基础针对自己薄弱环节总结01(循环之前的知识)
    CodeForces
    N年的经验在别人眼里是怎么看的?
    perl install-module.pl DateTime 执行无效问题的解决
    在Linux上使用iptables命令开启对外访问的网络端口
    Linux上安装Bugzilla4.4小记
    在Lotus Notes设置邮件转发
  • 原文地址:https://www.cnblogs.com/jing99/p/12515149.html
Copyright © 2020-2023  润新知