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网站,小编将为大家输出更多高质量的实用文章!