03-netty BootStrap学习小结

1 如何在抽象类中方法返回实体类?

类似于构造器的思路,调用方法后返回本身;

在这里插入图片描述

抽象类中通过泛型,引入了继承它本身的类,这种做法,可以帮助抽象类返回实现类;

2 使用ChannelFactory工厂的好处?

AbstractBootstrap {
private volatile ChannelFactory<? extends C> channelFactory;

这里提供了两种创建channel的思路:

  • 第一种直接设置channel

    这种方法使用默认的BootstrapChannelFactory构造,存在channel灵活性问题,如果是自己定义的channel则不会有很好的扩展性.

    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new BootstrapChannelFactory<C>(channelClass));
    }
  • 第二种设置工厂类的方式

这种方法的优势是创建何种channel可以由外部扩展决定!目前看到的ovsdb等南向协议使用第一种直接设置NioServerSocketChannel或者NioSocketChannel居多

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

3 有哪些options和attrs?具体含义?可否扩展?

在这里插入图片描述

3.1 options

  • options的一些具体值,如下所示,后续再关注选项在实现channel时候带来的变化!

在这里插入图片描述

如下所示为ovsdb中的一些选项设置:

            //设置tcp无延迟
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            //设置缓冲buf
            bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(65535, 65535, 65535));
            //设置后台日志???
            .option(ChannelOption.SO_BACKLOG, 100)

3.2 attr

attr目前用的并不多,个人认为这是用户基于channel做的一些定制属性,便于用户扩展和识别channel留下来的!

4 options和attrs如何与channel关联?

在Bootstrap和ServerBootstrap中都有一段类似的代码,bootstrap的初始化接口init,会把ChannelPipline\options\attr属性均初始化给Channel

在这里插入图片描述

5 Channel和handler如何关联?

上图init函数的第一行实际上已经给出了两者的关联关系, channel中包含了ChannelPipeline,而handler作为pipline里面的众多处理handler一起传给了p.addLast(handler());,但是具体的服务端和客户端的初始化是不同的,配置handler也有所不同;

下面以ovsdb代码为例展开说明:

5.1 bootstrap如何关联handler?

客户端方式设置的是一个ChannelInitializer的handler,然后在到里面添加了新的其他的handler!

在这里插入图片描述

5.2 ServerBootstrap如何关联handler?

我们可以看到服务端设置两个handler,这里可以这里可以这样理解:

  • 服务端主handler以打印日志为主:
    在这里插入图片描述

  • 子handler用于每个连接通道的处理,不是立马创建而是响应连接添加

在这里插入图片描述

这里的ServerBootstrapAcceptor就是一个channelHandler,当接收会话请求后,channelRead的工作就是添加后续需要的handler—这里有一个值得学习的地方,在运行过程中动态pipline中加入childhandler
在这里插入图片描述

6 Bootstrap连接最后一步

bootstrap的连接最后一步就是连接指定服务器或者开启端口接听连接,这里最主要的工作就是初始化channel以及发起连接或监听工作;

6.1 client Bootstrap

ChannelFuture future = bootstrap.connect(address, port).sync();
Channel channel = future.channel();

这里最重要的是两个处理,初始化channel,这里涉及到4-5节内容,不具体展开,以及最重要的doResolveAndConnect0--见第七节

在这里插入图片描述

6.2 server ServerBootstrap

这里是serverBootstrap的最后一步:

ChannelFuture channelFuture = serverBootstrap.bind(ip, port).sync();

ServerBootstrap的主要功能在doBind完成,这里最重要的操作也是两个:

  • initAndRegister-- 初始化channel,见4-5节内容;
  • doBind0 – 具体绑定事宜处理,见第8节内容;
 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 {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

最重要的方法是initAndRegister

7 客户端doResolveAndConnect0做了什么?

有了Channel,剩下的工作就是连接了,这里暂时把ChannelPromise的处理留意下,准备后续在研究,重点看看doConnect;

    private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        final Channel channel = connectPromise.channel();
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }

这里把connect的调用部分调用列出来,至于channel的处理逻辑,后续在专题讨论!

Bootstrap.java--doConnect0
io-Channel.java--connect
AbstractChannel.java--connect
DefaultChannelPipeline.java--tail.connect
AbstractChannelHandlerContext.java--connect
connect--next.invokeConnect
ChannelOutboundHandler--connect
H

8 服务端 doBind0做了什么?

如下所示为doBind0的代码:

    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

doBind0是在客户端连接请求到来后,开启channel的绑定工作.

这里也先给出调用逻辑,后续再做详细分析

AbstractBootstrap.java--doBind0
io-Channel.java--bind
AbstractChannel.java--bind
DefaultChannelPipeline.java--tail.bind
AbstractChannelHandlerContext.java--bind
connect--next.invokeBind
ChannelOutboundHandler--bind

9 遗留问题

1 ChannelPromise在doResolveAndConnect0的作用?

2 channel的connect和bind做了什么后续分析?

发布了88 篇原创文章 · 获赞 16 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/xinquanv1/article/details/100970965