Netty源码(六)之doBind0()方法的过程

上篇博客我们介绍了任务队列中调用过程,分别介绍的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方法,最后调用的TailContextbind方法,由于TailContext中没有这个方法,所以调用的是父类AbstractChannelHandlerContextbind方法,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;
  }
}

上面的代码会执行AbstractChannelHandlerContextinvokeChannelActive方法,传入的参数是头节点,我们继续跟进,具体的代码如下:

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();
      }
    }
  }
}

上面的channelAbstractChannel类,我们继续跟进对应的代码,具体的代码如下:

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
	@Override
  public Channel read() {
    pipeline.read();
    return this;
  }
}

上面的pipelineDefaultChannelPipeline,我们继续跟进对应的代码,具体的代码如下:

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中调用的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值