netty ServerBootstrap.bind方法怎么使用


您的位置:首页>产品资讯> 内容正文

netty ServerBootstrap.bind方法怎么使用

本篇内容介绍了“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 ServerBootstrap.bind方法怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注美国cn2网站,小编将为大家输出更多高质量的实用文章!

发布时间:2022-09-07

统计代码