文档章节

netty ServerBootstrap.bind方法解析

 大胖和二胖
发布于 2016/07/11 13:46
字数 1210
阅读 445
收藏 0

我们都知道,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又做了些什么。

 

 

 

© 著作权归作者所有

粉丝 23
博文 69
码字总数 50842
作品 0
沈阳
架构师
私信 提问
【原创】Netty 内核

作者:星巴刻 一、Netty 内核组 Netty 运行时包含了多个内核。在服务端程序中,需要分别创建 parent 和 child 两种内核: 1 个 parent 内核和 16 个 child 内核( 8 核 CPU系统下的默认数)。...

星巴刻
2017/11/22
0
0
Netty 源码研究

org.jboss.netty.bootstrap 本身 Netty 可以作为一个server存在的,因此他存在启动入口,他具有client启动,server启动以及connectionless 启动(比如UDP) 1.基类bootstrap:他包含Channel...

linugb118
2010/11/09
0
0
第二章:第一个Netty程序

本章介绍 获取Netty4最新版本 设置运行环境来构建和运行netty程序 创建一个基于Netty的服务器和客户端 拦截和处理异常 编写和运行Netty服务器和客户端 ----学习Netty是如何拦截和处理异常,服...

李矮矮
2016/09/23
87
0
目前为止最透彻的的Netty高性能原理和框架架构解析

1、引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件、...

小致Daddy
09/27
26
0
Netty源码分析(一)概览

准备将Netty的源码过一下,一来对自己是个总结消化的过程,二来希望对那些打算看Netty源码的人(已经熟悉Netty的Reactor模型)能有一些帮助。目前所看Netty版本是4.1.3.Final。 1 目录 - Ne...

乒乓狂魔
2016/08/17
795
0

没有更多内容

加载失败,请刷新页面

加载更多

领域驱动中的“贫血症和失忆症”

贫血症严重危害着人类健康,并且伴随有危险的副作用。当贫血领域对象被首次提出来时,它并不是一个博得赞美的词汇,它描述的是一个缺少内在行为领域对象。奇怪的是,人们对于贫血领域对象的态...

还仙
12分钟前
3
0
条码打印软件中标签预览正常打印无反应怎么解决

在使用条码打印软件制作标签时,有客户反馈,标签打印预览正常的,但是打印无反应,咨询是怎么回事?今天针对这个情况,可以参考以下方法进行解决。 一、预览正常情况下,打印没反应 (1)在条码...

中琅软件
22分钟前
3
0
判断字符串的时候

判断字符串的时候一定把常量房前边, //报警程度 String leve = vo.getDeviceAlertDeal().getWarnLevel(); if(("0").equals(leve)) { row.add("无报警"); }else if(("1").equals(leve)) { ro......

简小姐
22分钟前
5
0
Linux maven3.6.2 install

PS:安装 maven 之前请先安装 jdk 1.安装 wget 命令(安装过就不用了) yum -y install wget 2.寻找需要的 maven 版本 https://maven.apache.org/download.cgi 3.进入 /var/local 文件夹 cd...

东方神祇
24分钟前
3
0
Tomcat源码分析二:先看看Tomcat的整体架构

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server、Service、Connector、Container等组件,接下...

flygrk
27分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部