Bootstrap

Netty---BootStrap

Bootstrap类是Netty提供的一个工厂类,作用:Netty组件的组装,Netty程序的初始化。

一,概念    

1.父子通道

        在Netty中,每一个NioSocketChannel通道所封装的是Java Nio通道,java Nio下面就是操作系统封装的socket描述符。操作系统的底层socket描述符又分为:

        1.连接监听类型:放在服务器端,负责接收客户端的套接字连接请求,一个监听类型的socket描述符可以监听成千上万个连接请求。

        2.传输数据类型:负责传输数据,在同一条tcp中,服务器和客户端都有一个传输数据类型的scoket描述符

    所以在Netty中,NioServersocketChannel就是监听类型是,作为父通道。

                            NioSocketChannel是传输类型的,作为子通套。

 2.EventLoopGroup线程组

        在Netty中,一个EventLoop对于一个SubReactor子反应器,我们高效的netty肯定不是单线程的reactor反应器模式,肯定是多线程的,所以这里必然存在多个子反应器,对于子反应器的管理,就推出了EventLoopGroup线程组,在构建的时候,传入参数,多少个线程,就初始化多少个线程,一个子反应器里面 有一个Thread还有一个selector。如果不传入参数,那么默认是子反应器数量是cpu数量的2倍。

        对于netty,把服务端的监听,和处理分为了两个线程组,一个线程组专门负责监听和接收,被称为“包工头”另外一个专门负责Io事件的处理,也就是板砖,被称为“工人”,他们两个如何协调工作呢?这就是我们要引出的重点。

3.Bootstrap的启动流程

        如同我们第一节中demo代码内容,Bootstrap的启动分为八个步骤,实现了组件的组装,配置,以及服务器/客户端的启动。

            //1 设置反应器线程组
            b.group(bossLoopGroup,workerLoopGroup);
            //2 设置nio类型的通道
            b.channel(NioServerSocketChannel.class);
            //3 设置监听端口
            b.localAddress(serverPort);
            //4 设置通道的参数
            b.option(ChannelOption.SO_KEEPALIVE,true);
            b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            //5 装配子通道流水线
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel ch){
                    ch.pipeline().addLast(new NettyDiscardHandler());
                }
            });
            //6 开始绑定服务器,通过调用sync同步方法阻塞直到绑定成功
            ChannelFuture channelFuture=b.bind().sync();
            //7 等待通道关闭的异步任务结束
            ChannelFuture closeFuture =channelFuture.channel().closeFuture();
            closeFuture.sync();
            //8 关闭EventLoopGroup
            workerLoopGroup.shutdownGracefully();
            bossLoopGroup.shutdownGracefully();

     4.设置通道参数:它使用了bootstrap的option()方法,对于服务端来说,它是给bossGroup设置的选项,比如第一个:是否开启tcp底层心跳机制,true为开启。如果给子通道设置属性,则使用ChildOption()方法。

    5.装配子通道的Pipeline流水线:每个通道的子通道,都有一条流水线,它的内部有一个双向链表,装配流水线就是:将业务处理器handler实例加入到双向链表中。

        当父通道 成功接收 一个请求连接,并成功创建一个子通道之后,就会初始化子通道,就会调用此处的ChannelInitializer的实例,调用初始化方法。

(注意,我们使用serversocket进行监听,当收到一个请求的时候,我们返回的是一个socket,这个时候,我们就把socket发送给handler进行处理,所以在服务端,我们就有父子通道的存在,不要误以为子通道只在客户端)

     6.开始绑定服务器新连接的监听端口:返回一个端口绑定Netty的异步任务channelFuture,并没有给channelFuture异步任务添加回调监视器,而是阻塞异步任务,直到端口绑定任务执行完成。

二,源码

2.1 AbstractBootstrap

看一下bootStrap的结构,他分为client和server端的启动类,共同实现了AbstractBootStrap

我们先研究一下 父类AbstracBootStrap

重要字段:

volatile EventLoopGroup group;   //acceptor操作的 group
@SuppressWarnings("deprecation")  //这个注解表示已经过期,有更好的替代
private volatile ChannelFactory<? extends C> channelFactory; //channel工厂,生成channel
private volatile SocketAddress localAddress; //地址
validation purposes.

// 属性配置
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
//附加属性设置
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
//处理类
private volatile ChannelHandler handler;

2.2 ServerBootStrap

字段简单:

private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
// 流水线 handler
private volatile ChannelHandler childHandler;
从构造方法开始:  public ServerBootstrap() { }  空的。
1.反应器注入 group()
父类保存父Group,本类保存子Group
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    super.group(parentGroup);
    this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
    return this;
}
public B group(EventLoopGroup group) {
    this.group = group;
    return self();
}

2 设置channel类型,这个是必须的,不可省略,方法存在于父类中,直接调用channelFactory()方法,参数是实现了ChannelFactory接口的类,将我们传入的 channel.class中的构造方法扣出来了。

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

参数源码:

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(); //反射获取了参数的 构造方法
        ...
    }
//通过反射,构造方法,获取实例
@Override
public T newChannel() {
    return constructor.newInstance();
   ...抛异常
}

channelFactory()方法: 简单的把参数,保存到父类AbstractBootstrap中

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
   ...
    this.channelFactory = channelFactory;
    return self();
}

小总结:b.child()方法,就是生成了一个channelFactory,可以通过反射实例化我们传入的channel.class对象。

3.设置监听端口,可以通过loadAddress放入,也可以通过Bind()放入

loadAddress()方式,不管放入什么,最后对包装成为SocketAddress存放与父类AbstractBootstrap中

public B localAddress(SocketAddress localAddress) {
    this.localAddress = localAddress;
    return self();
}
public B localAddress(int inetPort) {
    return localAddress(new InetSocketAddress(inetPort));
}
public B localAddress(String inetHost, int inetPort) {
    return localAddress(SocketUtils.socketAddress(inetHost, inetPort));
}
public B localAddress(InetAddress inetHost, int inetPort) {
    return localAddress(new InetSocketAddress(inetHost, inetPort));
}

4.设置通道参数option(),准确说是父通道的参数,将参数放入options集合中

public <T> B option(ChannelOption<T> option, T value) {
    ObjectUtil.checkNotNull(option, "option");
    synchronized (options) {
        if (value == null) {  //通过null的方式,可以删除设置
            options.remove(option);
        } else {
            options.put(option, value);
        }
    }
    return self();
}

5.装配流水线,这个是必须的

public ServerBootstrap childHandler(ChannelHandler childHandler) {
    this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
    return this;
}

这个参数是重点,内容就是直接把值赋给字段

我们放入的是实现了channelInitializer<SocketChannel>的类,并且实现initChannel()方法,他的继承关系图

他的顶部是是channelHandler接口,和ServerBootStrap中方法参数匹配上了,但是这里的范型也很特殊,因为它关系到了initChannel()的参数,之后在分析

6.bind() 重要内容到达,前方高能,位于父类AbstractBootStrap中

首先,最基本的套路,如果设置了SocketAddress,则直接走该Bind()入口,如果没有则需要包装参数,再走这里

public ChannelFuture bind(SocketAddress localAddress) {
    validate();
    return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}

所以内容还是在doBind()中,参数是localAddress

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister(); //第一步,【入】
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }

    if (regFuture.isDone()) {//进了
        // At this point we know that the registration was complete and successful.
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
      ...
    }
}

重点是在第一个方法上面

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        //获得我们b.channel(...Class)传入的对象,也就是NioServerSocketChannel
        channel = channelFactory.newChannel();
        init(channel); // 调用初始化方法 委派模式,子类实现
    } ...
    //注解channel,调用的是 EventLoopGroup.register()方法 也就是把handler注册到selector中
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
       ...
    }
    return regFuture;
}

ServerBootStrap.init()

void init(Channel channel) { //初始化 b.channel(传入的对象)NioServerSocketChannel
    //参数配置
    setChannelOptions(channel, newOptionsArray(), logger);
    setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
    //获得流水线
    ChannelPipeline p = channel.pipeline();
    //缓存4个字段
    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler; //b.childHandler()放入
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
    }
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
    //给流水线,添加内容
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = config.handler(); //父类存放的handler 是一个LoginHandler
            if (handler != null) {
                pipeline.addLast(handler);  //【加入】
            }

            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {  //使用acceptor对我们导入的进行封装
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

;