• 【Netty】(6) ---源码ServerBootstrap


    【Netty】6 ---源码ServerBootstrap

    之前写了两篇与Bootstrap相关的文章,一篇是ServerBootstrap的父类,一篇是客户端Bootstrap类,博客地址:

    【Netty】源码AbstractBootstrap
    【Netty】源码 Bootstrap

    所以接下来 有关ServerBootstrap 源码的分析,如果上面已经分析过了,就不再陈述。

    一、概念

    ServerBootstrap可以理解为服务器启动的工厂类,我们可以通过它来完成服务器端的 Netty 初始化。
    作用职责:EventLoop初始化,channel的注册过程 ,关于pipeline的初始化,handler的添加过程,服务端连接分析。
    下面也先看下源码

    ​      // 定义一对线程组
    ​         // 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
    ​         EventLoopGroup bossGroup = new NioEventLoopGroup();
    ​         // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务
    ​         EventLoopGroup workerGroup = new NioEventLoopGroup();
    
         try {
             // netty服务器的创建, 辅助工具类,用于服务器通道的一系列配置
             ServerBootstrap serverBootstrap = new ServerBootstrap();
             serverBootstrap.group(bossGroup, workerGroup)           //绑定两个线程组
                            .channel(NioServerSocketChannel.class)   //指定NIO的模式
                            .handler(new LoggingHandler(LogLevel.INFO))
                            .childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup
    
             // 启动server,并且设置8088为启动的端口号,同时启动方式为同步
             ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
    
             // 监听关闭的channel,设置位同步方式
             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)方法

    这里和上一篇说的channl是差不多的,具体源码可以看上一篇就可以了,只是这里传入的类是NioServerSocketChannel,而客户端是NioSocketChannel,但他们都是通过类的反射
    机制获得类的对象的。同样真正用到该对象的时候,也是在bind(host)方法里。

    有关NioServerSocketChannel对象和之前的NioSocketChannel对象本身是没有讲过的。


    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;
        }
    

    有关handler和childHandler在哪个地方会被运用,等下将bind()方法的时候,我们在看他的源码。

    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 首先可以看到into的方法在父类(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));
                        }
                    });
                }
            });
        }
    

    源码博客推荐

    有些源码自己也没有去分析 比如:NioServerSocketChannel对象本身,ChannelPipeline对象。
    下面推荐源码专题博客
    1、Netty源码分析教程
    2、Netty学习笔记



    如果一个人充满快乐,正面的思想,那么好的人事物就会和他共鸣,而且被他吸引过来。同样,一个人老带悲伤,倒霉的事情也会跟过来。
    
                                                      ——在自己心情低落的时候,告诫自己不要把负能量带给别人。(大校14)
    
  • 相关阅读:
    嵌入式软件设计第11次实验报告-140201235-陈宇
    嵌入式软件设计第十次实验报告-140201235-陈宇
    嵌入式软件设计第九次实验报告-140201235-陈宇
    软件工程学-需求分析
    嵌入式软件设计第8次实验报告-140201235-陈宇
    嵌入式软件设计第7次实验报告-140201235-陈宇
    软件工程作业三:微软小娜APP的案例分析
    结构化分析(SA)加工逻辑工具
    软件工程作业二:需求分析
    软件工程作业一
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/10134069.html
Copyright © 2020-2023  润新知