共计 4227 个字符,预计需要花费 11 分钟才能阅读完成。
本篇内容介绍了“netty ServerBootstrap.bind 方法怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
我们都知道,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 方法怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!