【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<SocketChannel>() { // 客户端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<? extends C> channelClass) {
        // 实际上,返回对象的类型是ReflectiveChannelFactory
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }

    public B channelFactory(ChannelFactory<? extends C> 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<? 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);
        }
    }

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

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

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

    public <T> B option(ChannelOption<T> 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<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();

    public <T> B attr(AttributeKey<T> 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源码系列】服务端启动流程(三)绑定端口并启动

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容