【Netty源码系列】服务端启动流程(二)创建并初始化ServerBootstrap对象

经过上一篇【Netty源码系列】服务端启动流程(一)创建线程池组 可知大致创建线程池组的流程。而这一篇文章则是剖析 ServerBootstrap 的创建流程。这一个类是Netty服务端的“灵魂”!可以通过 ServerBootstrap 定制化配置线程池组、TCP参数、Handler等等,最后绑定端口即可启动Netty服务端。所以毫不夸张的说,ServerBootstrap 就是Netty服务端的灵魂人物!

先看下Netty服务端是如何创建并初始化ServerBootstrap的。

        // 创建 ServerBootstrap 对象
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup) // 绑定线程池组
                .channel(NioServerSocketChannel.class) // 服务端channel类型
                .option(ChannelOption.SO_BACKLOG, 100) // TCP配置
                .handler(new LoggingHandler(LogLevel.INFO)) // 服务端Handler
                .childHandler(new ChannelInitializer() { // 客户端Handler
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        //p.addLast(new LoggingHandler(LogLevel.INFO));
                        p.addLast(serverHandler);
                    }
                });

先简单介绍一下 ServerBootstrap 几个重要的方法:

group():设置主从线程池
channel():指定Channel类型
handler():配置ServerSocketChannel相关的handler(处理器)
option():配置ServerSocketChannel相关的TCP参数
attr(): 设置ServerSocketChannel的属性
childHandler():配置SocketChannel相关的handler
childOption():配置SocketChannel相关的TCP参数
childAttr():设置SocketChannel的属性

接下来,看一下上面几个核心的方法源码

设置主从线程池组——group()

group方法在ServerBootstrap中有两个重载方法,区别在于:一个参数的group方法指的是接收和处理客户端请求都是同一个线程池组;两个参数的group方法指的是第一个参数是bossGroup,第二个参数是workerGroup

    /**
     * 同一个线程池组负责接收和处理客户端请求
     */
    @Override
    public ServerBootstrap group(EventLoopGroup group) {
        return group(group, group);
    }

    /**
     * @param parentGroup 指的是bossGroup,负责接收客户端请求
     * @param childGroup 指的是workerGroup,负责处理客户端请求
     */
    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

可以看出,group(EventLoopGroup group)方法实际上也是调用group(EventLoopGroup parentGroup, EventLoopGroup childGroup),所以重点剖析两个参数的group方法

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        // 调用父类的group方法
        super.group(parentGroup);
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        // 赋值负责处理客户端请求的线程池组
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

    // AbstractBootstrap类
    public B group(EventLoopGroup group) {
        ObjectUtil.checkNotNull(group, "group");
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        // 赋值负责接收客户端请求的线程池组
        this.group = group;
        return self();
    }

其实可以看出,group方法并没有啥特别的逻辑,只不过就是赋值两个线程池组到对应的属性而已...

指定Channel类型——channel()

channel方法实际上是调用ServerBootstrap的父类AbstractBootstrap,通过channel方法赋值channelFactory对象,当ServerBootstrap启动的时候,就会通过channelFactory创建指定Channel对象(例子中指定的是NioServerSocketChannel,则Netty服务端启动的时候就会创建NioServerSocketChannel对象)

    public B channel(Class channelClass) {
        // 实际上,返回对象的类型是ReflectiveChannelFactory
        return channelFactory(new ReflectiveChannelFactory(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }

    public B channelFactory(ChannelFactory channelFactory) {
        ObjectUtil.checkNotNull(channelFactory, "channelFactory");
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }
        // 赋值channelFactory对象(实际上是ReflectiveChannelFactory)
        this.channelFactory = channelFactory;
        return self();
    }

    // ReflectiveChannelFactory类
    public ReflectiveChannelFactory(Class 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);
        }
    }

配置ServerSocketChannel相关的TCP参数——option()

这个方法就更简单了,其实就是将Netty服务端自定义的TCP参数“塞”进一个Map集合保存下来,当Netty服务端启动之后,就会根据自定义的服务端TCP参数创建ServerScoketChannel。

    // ServerSocketChannel相关TCP配置
    private final Map, Object> options = new LinkedHashMap, Object>();

    public  B option(ChannelOption option, T value) {
        ObjectUtil.checkNotNull(option, "option");
        synchronized (options) {
            if (value == null) {
                options.remove(option);
            } else {
                options.put(option, value);
            }
        }
        return self();
    }

设置ServerSocketChannel的属性——attr()

这个和上面的option逻辑一样,通过Map集合保存自定义的ServerSocketChannel属性,当服务端启动的时候就会从这个map中取出来创建对应的ServerSocketChannel。

    // ServerSocketChannel 的相关属性
    private final Map, Object> attrs = new ConcurrentHashMap, Object>();

    public  B attr(AttributeKey key, T value) {
        ObjectUtil.checkNotNull(key, "key");
        if (value == null) {
            attrs.remove(key);
        } else {
            attrs.put(key, value);
        }
        return self();
    }

以上都是针对ServerSocketChannel的相关配置,其实和SocketChannel的配置方法逻辑基本一致,所以不再重复赘述...

虽然ServerBootstrap对象的创建和配置源码简单,但只要开发者合理的配置,优化Netty服务端配置,其性能是十分强大的!正是通过这些“简单”的源码,使我更加佩服Netty框架的设计者,通过这么优雅,简单的方式就能够搭建起网络应用程序框架,不再像以前使用nio甚至使用io编程那样,苦恼性能和编码复杂性等问题。这是十分值得后人去学习和研究的!

以上就是创建并初始化ServerBootstrap对象大致流程,接下来就是服务端启动的核心源码,欲知后事如何,请看下篇:【Netty源码系列】服务端启动流程(三)绑定端口并启动

如果觉得文章不错的话,麻烦点个赞哈,你的鼓励就是我的动力!对于文章有哪里不清楚或者有误的地方,欢迎在评论区留言~

你可能感兴趣的:(【Netty源码系列】服务端启动流程(二)创建并初始化ServerBootstrap对象)