上篇博客我们介绍了任务队列中调用过程,分别介绍的register0
的调用过程,以及往pipeline
添加ServerBootstrapAcceptor
的过程。这两个任务已经介绍完了。这个时候还有一个任务就是doBind0
中添加的一个方法,这就是我们今天要介绍的方法。让我们重现对应的代码吧!由于我们每篇博客中都重现了服务端的代码,这篇博客我们就不重现了,我们直接看doBind0
方法,具体的代码如下:
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化和注册
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
//调用
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// 在触发channelregister()之前调用此方法。给用户处理程序设置的机会
// 管道在其channelRegistered()实现中。
// 这些任务最终被事件轮询线程同步调用
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
System.out.println(" channel.eventLoop().execute(new Runnable() ");
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
}
上面的方法熟悉不,看过我前几篇博客,应该对这块的代码很熟悉。不过今天的重点不是doBind
方法,今天的方法是doBind0
方法,这个时候我们发现,doBind0
中添加了一个任务到任务队列中去,所以在轮询的时候,一定会调用到,我们继续跟进对应的代码,后面添加的监听器是失败时候关闭调用的方法。这个时候我们不需要管,我们只需要管channel.bind(localAddress, promise)
方法,我们继续跟进对应的代码,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {}
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
}
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
//调用的是尾节点的bind方法
return tail.bind(localAddress, promise);
}
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
//传进来的localAddress不为空
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
//一些校验的操作
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
//从尾节点查找到下一个继承ChannelOutboundHandler,这儿由于我们的添加的节点是ChannelInboundHandler,原来的ServerBootstrapAcceptor一样是ChannelInboundHandler,所以这儿查找到的节点就是HeadContext
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
//取出来的是NioLoop
EventExecutor executor = next.executor();
//判断成立
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else { //假设不成立,直接丢进任务队列中去
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
}
上面的代码经过一连串的调用链,走来调用的是AbstractChannel
类中bind
方法,因为附加对象就是这个类,然后调用的AbstractChannelHandlerContext
类中的bind
方法,最后调用的TailContext
的bind
方法,由于TailContext
中没有这个方法,所以调用的是父类AbstractChannelHandlerContext
的bind
方法,isNotValidPromise(promise, false)
方法是进行一些校验,然后就会从尾节点开始向前找,找到第一个继承ChannelOutboundHandler
类的类,这个时候找到的就是HeadContext
类,然后不管进if
还是else
都会调用到next.invokeBind(localAddress, promise);
方法,记住这儿的next
是我们的HeadContext
类。唯一的区别就是如果调用的是else
中代码就是将这个方法添加到任务队列中去。这个时候我们继续跟进对应的代码,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
//走来会判断当前的节点是否添加完成了,由于这个是HeadContext,肯定是添加完成了
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
}
走来会先判断当前的节点是否添加完成了,由于是头节点,这儿应该是添加完成了,所以我们会执行((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
代码,这儿会调用HeadContext
类中的bind方法,这是因为在HeadContext
我们重写了ChannelOutboundHandler
中的bind
方法。我们继续跟进,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
unsafe.bind(localAddress, promise);
}
}
}
我们会发现调用的unsafe.bind(localAddress, promise);
方法,我们继续跟进对应的代码,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
//线程执行调用channelActive方法
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
}
}
上面的isActive
方法判断当前的通道是否打开,还有就是是否绑定的,很明显这个地方返回的是false
,因为我们下面就是执行对应绑定端口的方法,具体的代码如下:
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
//调用nio api绑定ip端口
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
}
这儿会调用java
原生的bind
方法,就会绑定对应的端口,bind
方法的第二参数用来表示最大的等待的客户端的连接数,原生的nio
的值为50,这儿可以自己设置,但是这儿默认是128,这儿执行绑定端口后,我们继续回到原来的方法,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
//wasActive=false
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//这个时候由于上面返回的false取反就是true,然后再执行isActive()方法,这个返回的true,所以都是true
if (!wasActive && isActive()) {
//线程执行调用channelActive方法
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
}
}
下面的那个判断,由于上面取得wasActive
的值为false
,这儿取反就是true
,然后继续执行isActive()
方法,由于已经绑定了,所以这儿返回的是true
,于是就执行if
判断中的方法,于是会执行invokeLater
方法,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
private void invokeLater(Runnable task) {
try {
//将任务放入到任务队列中去
eventLoop().execute(task);
} catch (RejectedExecutionException e) {
logger.warn("Can't invoke task later as EventLoop rejected it", e);
}
}
}
}
上面的代码将pipeline.fireChannelActive();添加到任务队列中,在任务轮训的时候,一定会调用到该方法。这个时候会返回到原来的地方,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
//省略部分代码
safeSetSuccess(promise);
}
}
}
上面的代码会执行safeSetSuccess(promise);
方法,由于之前在register0
方法中已经执行对应的代码,所以这儿返回的false
,到此这个doBind0
方法就执行完了,由于之前又添加了一个任务到任务队列中去了,我们继续跟进对应的代码,刚刚添加的任务的是pipeline.fireChannelActive();
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline fireChannelActive() {
//会执行头节点的invokeChannelActive方法
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
}
上面的代码会执行AbstractChannelHandlerContext
的invokeChannelActive
方法,传入的参数是头节点,我们继续跟进,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}
}
上面的代码无论如何都会执行invokeChannelActive
方法,只不过一个是添加到任务队列中执行,一个是直接执行,我们继续跟进对应的代码,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
private void invokeChannelActive() {
//判断这个节点是否添加完成了,很明显这个头节点是添加完成的。
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
}
走来是判断这个头节点是否添加完成,很明显这儿是添加完成了,然后就会调用头节点的channelActive(this);
方法,我们继续跟进对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void channelActive(ChannelHandlerContext ctx) {
//向下一个节点传播,继续执行下一个节点的channelActive
ctx.fireChannelActive();
readIfIsAutoRead();
}
}
}
上面的方法走来是向下传播执行对应的下一个节点的channelActive
的方法,这儿找到是继承ChannelInboundHandler
的节点中的channelActive
方法。我们还是看下对应的代码,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
@Override
public ChannelHandlerContext fireChannelActive() {
invokeChannelActive(findContextInbound(MASK_CHANNEL_ACTIVE));
return this;
}
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
//调用
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}
private void invokeChannelActive() {
if (invokeHandler()) {
try {
//调用
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
}
上面的代码进过一系列的调用,最终会调用HeadContext
下一个继承ChannelInboundHandler
的类中的channelActive
的方法,由于我们这儿添加了一个继承ChannelInboundHandler
类的节点,所以这儿会调用我们自己书写的类中channelActive
的方法。然后就会返回到原来执行代码的地方,具体如下:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void channelActive(ChannelHandlerContext ctx) {
//向下一个节点传播,继续执行下一个节点的channelActive
ctx.fireChannelActive();
readIfIsAutoRead();
}
}
}
上面的代码会执行readIfIsAutoRead
方法,我们继续跟进去,查看对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
}
}
上面的channel
是AbstractChannel
类,我们继续跟进对应的代码,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
@Override
public Channel read() {
pipeline.read();
return this;
}
}
上面的pipeline
是DefaultChannelPipeline
,我们继续跟进对应的代码,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline read() {
tail.read();
return this;
}
}
上面是调用尾节点的read
方法,我们继续跟进对应的代码,由于tail节点中没有实现read
方法,所以调用的是父类AbstractChannelHandlerContext
中的read
方法,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
@Override
public ChannelHandlerContext read() {
//从尾节点开始向前找到第一个继承ChannelOutboundHandler的类,很明显这儿又是头节点
final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeReadTask);
}
return this;
}
}
上面的代码刚开始从尾节点开始向前找到第一个继承ChannelOutboundHandler
的类,很明显这儿又是头节点,然后会调用next.invokeRead();
方法,我们继续跟进去看看,具体的代码如下:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
}
又是差不多的方法,很明显会调用read
方法,我们继续跟进去看看,具体的代码如下:
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
}
我们继续跟进去看看,具体的代码如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
}
}
上面的代码会执行 doBeginRead();
方法,我们继续跟进去看看,具体的代码如下:
public abstract class AbstractNioChannel extends AbstractChannel {
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
//校验selectionKey是否合法
if (!selectionKey.isValid()) {
return;
}
//准备读的标志
readPending = true;
//前面都没有添加感兴趣的事件,所以这儿获取的是0
final int interestOps = selectionKey.interestOps();
//0与任何数是0
if ((interestOps & readInterestOp) == 0) {
//0或任何数就是任何数。
selectionKey.interestOps(interestOps | readInterestOp);
}
}
}
终于到了我们的添加感兴趣的事件添加了,这儿添加了连接事件,到此整个服务端就启动起来了。我们可以看看在原生的Nio
的启动过程,再来看看Netty
中一一对应的代码,具体如下:
public TcpReactor(int port) throws Exception {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(port);
//在ServerSocketChannel绑定监听端口
serverSocketChannel.socket().bind(address);
//设置ServerSocketChannel为非阻塞
serverSocketChannel.configureBlocking(false);
// ServerSocketChannel向selector注册一个OP_ACCEPT事件,然后返回该通道的key
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//给定key一个附件的Acceptor对象
selectionKey.attach(new Acceptor(serverSocketChannel, selector));
}
第一个Selector.open();
在创建NioEventLoop
对象的时候打开的。
第二个ServerSocketChannel.open();
在创建NioServerSocketChannel
的时候打开的
第三个new InetSocketAddress(port);
在刚开始调用bind
方法就讲这个值给创建了
第四个serverSocketChannel.configureBlocking(false);
设置非阻塞在创建AbstractNioChannel
的时候调用了
第五个selectionKey.attach(new Acceptor(serverSocketChannel, selector));
在AbstractChannel
中调用的register0
中调用的doRegister();
这儿只注册0个感兴趣的事件和一个附加对象,就是AbstractNioChannel
。
第六个serverSocketChannel.socket().bind(address);
在调用NioServerSocketChannel
中调用doBind0
方法的时候调用,并且指定最大的等待的连接数为128
最后一个添加感兴趣的事件为连接事件,在AbstractNioChannel
中的doBeginRead
中调用的