netty ServerBootstrap.bind方法解析

我们都知道,netty是基于java nio的,那么具体的,java nio当中的一些具体元素,比如bind,selector,channel等等具体元素,在netty当中又是如何出现的。今天我们来找一找。

首先从ServerBootstrap.bind()方法开始。

该方法的具体实现在AbstractBootstrap当中,直接看到doBind方法,

final ChannelFuture regFuture = initAndRegister();

看看 initAndRegister 这里面具体干了点什么,

首先是创建了一个channel,并且从boss group当中分配一个进程给这个新创建的channel。

Channel channel;
try {
    channel = createChannel();
} catch (Throwable t) {
    return VoidChannel.INSTANCE.newFailedFuture(t);
}

然后是初始化的工作,具体内容这里暂不深究。

try {
    init(channel);
} catch (Throwable t) {
    channel.unsafe().closeForcibly();
    return channel.newFailedFuture(t);
}

然后是注册,首先得到一个promise,这个东西按照我的理解,就是为了异步的返回各种操作的执行结果,后续我们会经常见到它。然后执行register,我们又发现一个很诡异的东西unsafe,
这个东西是创建channel的时候带出来的,具体作用是跟java nio打交道,注入bind,register等操作,都在这里面完成,为什么弄这么个东西,目前不明,以后再说。

ChannelPromise regFuture = channel.newPromise();
channel.unsafe().register(regFuture);
if (regFuture.cause() != null) {
    if (channel.isRegistered()) {
        channel.close();
    } else {
        channel.unsafe().closeForcibly();
    }
}

然后是AbstractChannel$AbstractUnsafe.register(),第一个判断很有意思,我们可以这样理解,如果当前channel对应的 eventLoop 已经在执行了,也就是说,当前代码已经处在了一个子线程当中,那么直接调用 register0 方法,否则,新起一个线程执行。

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    promise.setFailure(t);
                }
            }

然后我们看一下 register0 里面具体干了些什么:

            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!ensureOpen(promise)) {
                    return;
                }
                doRegister();
                registered = true;
                promise.setSuccess();
                pipeline.fireChannelRegistered();
                if (isActive()) {
                    pipeline.fireChannelActive();
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                if (!promise.tryFailure(t)) {
                    logger.warn(
                            "Tried to fail the registration promise, but it is complete already. " +
                                    "Swallowing the cause of the registration failure:", t);
                }
            }

首先通过promise检查了一下当前channel的状态,然后执行doRegister,这个方法在AbstractNioChannel当中实现, 主要内容如下:

selectionKey = javaChannel().register(eventLoop().selector, 0, this);

好了目前我们明确了2个问题,第一selector存在于 eventLoop 当中,具体地说,应该是在boss loop当中。第二个问题,在执行register的时候,还没有执行bind。

在之后设置了promise,并通过pipeline出发了ChannelRegistered()事件。

下面再回到 AbstractBootstrap .doBind方法,第一步,初始化和注册selector的过程都已经完成了。

        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        final ChannelPromise promise;
        if (regFuture.isDone()) {
            promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    doBind0(regFuture, channel, localAddress, promise);
                }
            });
        }

怎么理解上面那段代码,尤其是 if (regFuture.isDone()) 之后的部分,我们前面提到,具体的register过程可能是在一个子线程当中执行的,所以这里需要等待register完成,才能进行下一步bind操作。

看doBind0的实现,这里还是直接起一个线程来执行bind操作。

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

看 channel.bind ,方法实现在AbstractChannel当中,很有趣,直接通过pipeline来做这个bind。那么我们来看一下pipeline.bind,实现在DefaultChannelPipeline当中,我们看到的是tail.bind,那么很显然,是要反向遍历链表,最终通过head去执行bind。那么我们看看HeadHandler.bind方法,unsafe.bind(localAddress, promise); 通过查找代码,我们发现, unsafe 就是最初我们创建的Server channel的unsafe,这是同一个东西。

我们再看一下AbstractChannel$AbstractUnsafe的bind方法,这里面涉及到一些对操作系统信息的读取和判断,然后调用了NioServerSocketChannel.doBind()方法,具体的真正的,java nio的bind操作,就在这里执行。

最后一件事,java nio当中的channel在netty当中是怎么出现的。很简单,看一下NioServerSocketChannel这个类,这个当中有一个newSocket方法,直接返回一个java 的ServerSocketChannel。

以上,我们基本达到了我们此次代码分析的目的,搞清楚了,在netty服务器启动的时候,具体干了些什么,

在netty当中找到了java nio当中的一些具体元素。

同时,我们找到了handler当中2个具体的event,bind和channelRegistered具体的发起时机。

另外,我们初步了解了netty提供的异步返回机制,ChannelFuture究竟是怎么工作的。

下一步,我们应该看看,在一个新的连接到来的时候。netty又做了些什么。

 

 

 

转载于:https://my.oschina.net/dongtianxi/blog/709558

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值