• Netty源码学习系列之3-ServerBootstrap的初始化


    前言

        根据前文我们知道,NioEventLoopGroup和NioEventLoop是netty对Reactor线程模型的实现,而本文要说的ServerBootstrap是对上面二者的整合与调用,是一个统筹者和协调者。具体netty使用的是Reactor单线程模型还是多线程模型、抑或者主从多线程模型,都是ServerBootstrap的不同配置决定的。

        下面照例粘贴一下示例demo(以Reactor多线程模式构建),开始正文。

     1 public class NettyDemo1 {
     2     // netty服务端的一般性写法
     3     public static void main(String[] args) {
     4         EventLoopGroup boss = new NioEventLoopGroup(1);
     5         EventLoopGroup worker = new NioEventLoopGroup();
     6         try {
     7             ServerBootstrap bootstrap = new ServerBootstrap();
     8             bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
     9                     .option(ChannelOption.SO_BACKLOG, 100)
    10                     .childHandler(new ChannelInitializer<SocketChannel>() {
    11                         @Override
    12                         protected void initChannel(SocketChannel socketChannel) throws Exception {
    13                             ChannelPipeline pipeline = socketChannel.pipeline();
    14                             pipeline.addLast(new StringDecoder());
    15                             pipeline.addLast(new StringEncoder());
    16                             pipeline.addLast(new NettyServerHandler());
    17                         }
    18                     });
    19             ChannelFuture channelFuture = bootstrap.bind(90);
    20             channelFuture.channel().closeFuture().sync();
    21         } catch (Exception e) {
    22             e.printStackTrace();
    23         } finally {
    24             boss.shutdownGracefully();
    25             worker.shutdownGracefully();
    26         }
    27     }
    28 }

    一、ServerBootstrap的初始化

        ServerBootstrap的无参构造器啥都没做,它使用的build模式给属性赋值,即上面示例中看到的,每执行一个赋值方法都会返回当前对象的引用使得可以继续链式调用。下面挨个方法追踪。

    1 public ServerBootstrap() { }

    1、ServerBootstrap.group方法

        ServerBootstrap有两个可用重载group方法(如下的两个),其中接收一个group入参的方法会调用有两个入参的group方法,只是两个参数传同一个group。这两个group方法决定了netty使用的Reactor线程模型的类型,一个group入参的方法对应Reactor单线程模型,两个入参且不是同一个group的方法对应Reactor多线程模型或主从多线程模型(具体是哪一种取决于实例化parentGroup时的线程数)。此处只是提一下,先有个印象,后面会对线程模型进行详细研究。

    1 public ServerBootstrap group(EventLoopGroup group) {
    2         return group(group, group);
    3     }
    1 public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    2         super.group(parentGroup);
    3         ObjectUtil.checkNotNull(childGroup, "childGroup");
    4         if (this.childGroup != null) {
    5             throw new IllegalStateException("childGroup set already");
    6         }
    7         this.childGroup = childGroup;
    8         return this;
    9     }

        可以看到上述group方法对两个入参进行了不同位置的赋值,将第一个参数parentGroup传给了父类AbstractBootstrap的group方法,如下,即最终赋值给了AbstractBootstrap中的group属性。第二个参数直接赋值给了ServerBootstrap的childGroup属性。

    1 public B group(EventLoopGroup group) {
    2         ObjectUtil.checkNotNull(group, "group");
    3         if (this.group != null) {
    4             throw new IllegalStateException("group set already");
    5         }
    6         this.group = group;
    7         return self();
    8     }

    2、ServerBootstrap.option/childOption方法和ServerBootstrap.attr/childAttr方法

        这四个方法只是做了属性的赋值,分别赋值给了AbstractBootstrap的options属性和attrs属性以及ServerBootstrap的childOptions属性和childAttrs属性。

     1 public <T> B option(ChannelOption<T> option, T value) {
     2         ObjectUtil.checkNotNull(option, "option");
     3         if (value == null) {
     4             synchronized (options) {
     5                 options.remove(option);
     6             }
     7         } else {
     8             synchronized (options) {
     9                 options.put(option, value);
    10             }
    11         }
    12         return self();
    13     }
     1 public <T> B attr(AttributeKey<T> key, T value) {
     2         ObjectUtil.checkNotNull(key, "key");
     3         if (value == null) {
     4             synchronized (attrs) {
     5                 attrs.remove(key);
     6             }
     7         } else {
     8             synchronized (attrs) {
     9                 attrs.put(key, value);
    10             }
    11         }
    12         return self();
    13     }

    3、ServerBootstrap.channel方法

        调用的是父类AbstractBootstrap的channel方法:

    1 public B channel(Class<? extends C> channelClass) {
    2         return channelFactory(new ReflectiveChannelFactory<C>(
    3                 ObjectUtil.checkNotNull(channelClass, "channelClass")
    4         ));
    5     }

        可以看到先封装成了一个ReflectiveChannelFactory对象,然后调用channelFactory方法,下面挨个看。ReflectiveChannelFactory的构造器如下,可见就是将传入class对象的构造器取出来赋值,此时constructor存放的就是NioServerSocketChannel的构造器。

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
            ObjectUtil.checkNotNull(clazz, "clazz");
            try {
                this.constructor = clazz.getConstructor();
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                        " does not have a public non-arg constructor", e);
            }
        }

        channelFactory方法的工作是将上面创建的ReflectiveChannelFactory对象赋值给AbstractBootstrap的channelFactory属性:

    1 public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    2         ObjectUtil.checkNotNull(channelFactory, "channelFactory");
    3         if (this.channelFactory != null) {
    4             throw new IllegalStateException("channelFactory set already");
    5         }
    6 
    7         this.channelFactory = channelFactory;
    8         return self();
    9     }

    4、ServerBootstrap.handler方法和ServerBootstrap.childHandler方法

        handler方法的入参赋值给了AbstractBootstrap的handler属性,childHandler方法的入参赋值给了ServerBootstrap的childHandler属性。看到这里想必园友们也能看出ServerBootstrap的赋值规律了,凡是child开头的都放在ServerBootstrap中,而不带child的大多放在其父类ABstractBootstrap中。

    1 public B handler(ChannelHandler handler) {
    2         this.handler = ObjectUtil.checkNotNull(handler, "handler");
    3         return self();
    4     }
    1 public ServerBootstrap childHandler(ChannelHandler childHandler) {
    2         this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
    3         return this;
    4     }

    5、完成赋值后ServerBootstrap的快照图

    小结

        ServerBootstrap的初始化过程看起来赋了很多值,但都只是做了准备工作,看起来轻松又简单,但请注意,这是暴风雨前宁静。前面的各种赋值到底有什么用处?很多属性分为有child前缀和没有child前缀,这样设置又有什么意图?下一期将进入ServerBootstrap的bind方法,这是netty的深水区,很多谜底也将在这里得到揭晓,敬请期待!

  • 相关阅读:
    SignalR了解
    轮询、长轮询、长连接、socket连接、WebSocket
    WebSocket
    FileSaver.js 实现浏览器文件导出
    上传文件调用webapi方式
    JS离开页面 弹窗
    微信公众号开发 VS2015本地调试
    C# 微信 企业号通知消息
    nginx防止DDOS攻击配置
    如何在终端使用后台运行模式启动一个Linux应用程序
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/13027161.html
Copyright © 2020-2023  润新知