Netty源码分析——ServerBootstrap

基于Netty源代码版本:netty-all-4.1.33.Final

前言

BootStrap在netty的应用程序中负责引导服务器和客户端。netty包含了两种不同类型的引导:

  • 1、使用服务器的ServerBootStrap,用于接受客户端的连接以及为已接受的连接创建子通道。
  • 2、用于客户端的BootStrap,不接受新的连接,并且是在父通道类完成一些操作。

Netty服务端示例:

public class MyServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); 
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap(); 
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .handler(new SimpleServerHandler())
                    .childHandler(new MyServerInitializer())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

private static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
    }
}

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyServerHandler());
    }
}

上面这段代码展示了服务端的一个基本步骤:

  • (1)、 初始化用于Acceptor的主"线程池"以及用于I/O工作的从"线程池";
  • (2)、 初始化ServerBootstrap实例, 此实例是netty服务端应用开发的入口,也是本篇介绍的重点, 下面我们会深入分析;
  • (3)、 通过ServerBootstrap的group方法,设置(1)中初始化的主从"线程池";
  • (4)、 指定通道channel的类型,由于是服务端,故而是NioServerSocketChannel;
  • (5)、 设置ServerSocketChannel的处理器(此处不详述,后面的系列会进行深入分析)
  • (6)、 设置子通道也就是SocketChannel的处理器, 其内部是实际业务开发的"主战场"(此处不详述,后面的系列会进行深入分析)
  • (7)、 配置ServerSocketChannel的选项
  • (8)、 配置子通道也就是SocketChannel的选项
  • (9)、 绑定并侦听某个端口

Netty客户端示例:

public class MyClient {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
            .channel(NioSocketChannel.class)
            .option(ChannelOption.SO_KEEPALIVE, true);
            .handler(new MyClientInitializer());
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            eventLoopGroup.shutdownGracefully();
        }

    }
}

客户端的开发步骤和服务端都差不多:

  • (1)、 初始化用于连接及I/O工作的"线程池";
  • (2)、 初始化Bootstrap实例, 此实例是netty客户端应用开发的入口,也是本篇介绍的重点, 下面我们会深入分析;
  • (3)、 通过Bootstrap的group方法,设置(1)中初始化的"线程池";
  • (4)、 指定通道channel的类型,由于是客户端,故而是NioSocketChannel;
  • (5)、 设置SocketChannel的选项(此处不详述,后面的系列会进行深入分析);
  • (6)、 设置SocketChannel的处理器, 其内部是实际业务开发的"主战场"(此处不详述,后面的系列会进行深入分析);
  • (7)、 连接指定的服务地址;

通过对上面服务端及客户端代码分析,Bootstrap是Netty应用开发的入口,如果想要理解Netty内部的实现细节,那么有必要先了解一下Bootstrap内部的实现机制。

首先我们先看一下ServerBootstrap及Bootstrap的类继承结构图:


通过类图我们知道AbstractBootstrap类是ServerBootstrap及Bootstrap的基类
1、 首先看看服务端的serverBootstrap.group(bossGroup, workerGroup):
调用ServerBootstrap的group方法,设置react模式的主线程池 以及 IO 操作线程池,ServerBootstrap中的group代码如下:

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    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;
}

在group方法中,会继续调用父类的group方法,而通过类继承图我们知道,super.group(parentGroup)其实调用的就是AbstractBootstrap的group方法。AbstractBootstrap中group代码如下:

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

通过以上分析,我们知道了AbstractBootstrap中定义了主线程池group的引用,而子线程池childGroup的引用是定义在ServerBootstrap中。
即将workerGroup保存在 ServerBootstrap对象的childGroup属性上。 bossGroup保存在ServerBootstrap对象的group属性上。

当我们查看客户端Bootstrap的group方法时,我们发现,其是直接调用的父类AbstractBoostrap的group方法。

2、示例代码中的 channel()方法
无论是服务端还是客户端,channel调用的都是基类的channel方法,其实现细节如下:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

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

public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
    return channelFactory((ChannelFactory<C>) channelFactory);
}

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方法内部,只是初始化了一个用于生产指定channel类型的工厂实例。
函数功能:设置父类属性channelFactory 为: ReflectiveChannelFactory类的对象。其中这里ReflectiveChannelFactory对象中包括一个clazz属性为:NioServerSocketChannel.class。

/**
 * 接受新连接的基于NIO选择器的实现
 */
public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
    ......
}

并且ReflectiveChannelFactory中提供 newChannel()方法,我们可以看到 clazz.newInstance(),主要是通过反射来实例化NioServerSocketChannel.class

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    private final Constructor<? extends T> constructor;

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

    @Override
    public T newChannel() {
        try {
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

    @Override
    public String toString() {
        return StringUtil.simpleClassName(ReflectiveChannelFactory.class) +
                '(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)";
    }
}

ServerBootstrap.childHandler(new MyServerInitializer())

/**
 * 设置ChannelHandler,用于为Channel的请求提供服务。
 */
public ServerBootstrap childHandler(ChannelHandler childHandler) {
    if (childHandler == null) {
        throw new NullPointerException("childHandler");
    }
    this.childHandler = childHandler;
    return this;
}

ServerBootstrap.handler(new SimpleServerHandler())

final ChannelHandler handler() {
    return handler;
}

bootstrap.handler(new MyClientInitializer())

/**
 * 用于服务请求的ChannelHandler
 */
public B handler(ChannelHandler handler) {
    if (handler == null) {
        throw new NullPointerException("handler");
    }
    this.handler = handler;
    return self();
}

ServerBootstrap的option

这里调用的是父类的AbstractBootstrap的option()方法,源码如下:

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

其中最重要的一行代码就是:
options.put(option, value);
这里用到了options这个参数,在AbstractBootstrap的定义如下:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
可知是私有变量,而且是一个Map集合。这个变量主要是设置TCP连接中的一些可选项,而且这些属性是作用于每一个连接到服务器被创建的channel。

ServerBootstrap的childOption

这里调用的是父类的ServerBootstrap的childOption()方法,源码如下:

public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    if (childOption == null) {
        throw new NullPointerException("childOption");
    }
    if (value == null) {
        synchronized (childOptions) {
            childOptions.remove(childOption);
        }
    } else {
        synchronized (childOptions) {
            childOptions.put(childOption, value);
        }
    }
    return this;
}

这个函数功能与option()函数几乎一样,唯一的区别是该属性设定只作用于被acceptor(也就是boss EventLoopGroup)接收之后的channel。

总结:
比较简单哈,主要是将我们提供的参数设置到其相应的对象属性中去了。 因为后面会用到如下的几个属性,因此最好知道下,这些属性是何时以及在那里赋值的。

  • 1、group:workerGroup保存在 ServerBootstrap对象的childGroup属性上。 bossGroup保存在ServerBootstrap对象的group属性上
  • 2、channelFactory:BootstrapChannelFactory类的对象(clazz属性为:NioServerSocketChannel.class)
  • 3、handler:SimpleServerHandler
  • 4、childHandler
  • 5、option
  • 6、childOption

参考:
https://www.cnblogs.com/java-chen-hao/p/11459808.html

https://www.cnblogs.com/itdriver/p/8149913.html

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

推荐阅读更多精彩内容