当前位置: 代码迷 >> 综合 >> Netty 源码分析一ServerBootstrap的bind方法
  详细解决方案

Netty 源码分析一ServerBootstrap的bind方法

热度:67   发布时间:2023-09-05 18:36:29.0

###################################################################################
后续文章中都会对一些代码逻辑进行说明,但原文的英文注释一般不会直译,进行保留,只会说明那些没有注释的地方
###################################################################################

1前言

      前面我们已经了解了NioEventLoopGroup这个类的创建流程以及其中的业务。如果没有了解的可以参看这个连接:Netty 源码分析一 NioEventLoopGroup创建逻辑
      而看完上面的类创建后,我们需要了解更多的该类功能,而该类的功能我们不可能一个方法一个方法的看其被什么地方调用,这样太乱,我们还是按DiscardServer类中的代码逻辑来看

ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();if (sslCtx != null) {p.addLast(sslCtx.newHandler(ch.alloc()));}p.addLast(new DiscardServerHandler());}});

      上面这一块代码跟NioEventLoopGroup相关的也就是调用ServerBootstrap 类的group方法,这个方法其实只是进行了一些内部变量的赋值,没有什么特殊的额外的逻辑。那我们就再看bind方法

b.bind(PORT)

2 bind方法逻辑

2.1 方法的执行的序列图

Netty 源码分析一ServerBootstrap的bind方法
      从其中的序列图中可以看出,主要的逻辑还是在AbstractBootstrap这个父类的doBind(final SocketAddress localAddress)方法中来执行的;我们来具体看下这块的代码

2.1.1 doBind方法

private ChannelFuture doBind(final SocketAddress localAddress) {// 这里就是根据b.channel(NioServerSocketChannel.class)设置的渠道类型实例化该类,// 并且对这个类进行一些初始化设置,具体的大致流程可以参看上面的序列图中4~11这些步骤,// 返回的实际上是DefaultChannelPromise这个对象.final ChannelFuture regFuture = initAndRegister();// 实际上调用的是DefaultChannelPromise 类中的channel,而该类中设置的channel是NioServerSocketChannelfinal Channel channel = regFuture.channel();if (regFuture.cause() != null) {// 如果前端register没有问题,此处就不会进来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() {@Overridepublic 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/2586promise.registered();doBind0(regFuture, channel, localAddress, promise);}}});return promise;}}

1. doBind方法内部的initAndRegister 方法
      上面方法代码第一条就是执行initAndRegister方法,而该方法的大致逻辑
就是根据b.channel(NioServerSocketChannel.class)设置的渠道类型实例化该NioServerSocketChannel类,并且对这个类进行一些初始化设置,具体的大致流程可以参看上面的序列图中12~15这些步骤,而详细的代码如下所示:

final ChannelFuture initAndRegister() {Channel channel = null;try {// 实例化该NioServerSocketChannel类channel = channelFactory.newChannel();// 调用其子类ServerBootstrap中的一些ServerSocketChannel渠道的设置init(channel);} catch (Throwable t) {if (channel != null) {// channel can be null if newChannel crashed (eg SocketException("too many open files"))channel.unsafe().closeForcibly();// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutorreturn new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);}// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutorreturn new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);}// 1. config()是调用其父类的这个重写方法,而该方法实际上创建的是ServerBootstrapConfig(ServerBootstrap) 这个类; // 2. ServerBootstrapConfig.group()方法实际上调用的就是ServerBootstrap类中赋值的parentGroup所对应的NioEventLoopGroup这个对象;// 3. register(channel), 实际上执行的就是NioEventLoopGroup类的register方法,传入的是 NioServerSocketChannel这个对象 // 4. 最后返回的regFuture 是DefaultChannelPromise这个类的对象数据ChannelFuture regFuture = config().group().register(channel);if (regFuture.cause() != null) {if (channel.isRegistered()) {channel.close();} else {channel.unsafe().closeForcibly();}}// If we are here and the promise is not failed, it's one of the following cases:// 1) If we attempted registration from the event loop, the registration has been completed at this point.// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.// 2) If we attempted registration from the other thread, the registration request has been successfully// added to the event loop's task queue for later execution.// i.e. It's safe to attempt bind() or connect() now:// because bind() or connect() will be executed *after* the scheduled registration task is executed// because register(), bind(), and connect() are all bound to the same thread.return regFuture;}

      至此我们可以看到再次使用NioEventLoopGroup 类的register(Channel) 方法的逻辑,这个里面具体执行了哪些业务,我们放到后续的文章中进行说明,在此先省略;我们继续看后续的业务执行。

1.1 initAndRegister 内部执行的 regFuture.cause()
      而对于regFuture.cause()这句代码,实际上执行的是如下的代码

@Overridepublic Throwable cause() {return cause0(result);}private Throwable cause0(Object result) {// 如果前面在执行config().group().register(channel)这行代码注册成功后,// result里面放入的只是一个Object对象,返回result类型不会是CauseHolder,因为方法返回的是nullif (!(result instanceof CauseHolder)) {return null;}if (result == CANCELLATION_CAUSE_HOLDER) {CancellationException ce = new LeanCancellationException();if (RESULT_UPDATER.compareAndSet(this, CANCELLATION_CAUSE_HOLDER, new CauseHolder(ce))) {return ce;}result = this.result;}return ((CauseHolder) result).cause;}

1.2 initAndRegister 内部的init 方法执行
      init方法我们通过序列图也可以看出来,它实际上执行的是ServerBootstrap 类中实现的init方法,具体的代码如下所示:

@Overridevoid init(Channel channel) {// channel 就是前面initAndRegister方法中setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger);setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0)));ChannelPipeline p = channel.pipeline();final EventLoopGroup currentChildGroup = childGroup;final ChannelHandler currentChildHandler = childHandler;final Entry<ChannelOption<?>, Object>[] currentChildOptions =childOptions.entrySet().toArray(newOptionArray(0));final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(final Channel ch) {final ChannelPipeline pipeline = ch.pipeline();ChannelHandler handler = config.handler();if (handler != null) {pipeline.addLast(handler);}ch.eventLoop().execute(new Runnable() {@Overridepublic void run() {pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));}});}});}
  1. doBind方法中doBind0方法
          当initAndRegister 方法执行完后,我们通过前面的doBind方法的代码也可以看出在NioServerSocketChannel通道注册成功后,会执行doBind0这个方法,而这个方法中大致处理的内容就是当渠道注册成功后,就会进行地址的绑定,并且会添加一个关闭通道时的监听器
private static void doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up// the pipeline in its channelRegistered() implementation.channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {if (regFuture.isSuccess()) {// 实际上执行的是NioServerSocketChannel的channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});}

      至此我们分析了ServerBootstrap的bind方法主要执行的逻辑,而在这个执行逻辑中还涉及到了以下几点的执行:
      NioEventLoopGroup.register 方法的逻辑
      NioServerSocketChannel.(SocketAddress localAddress, ChannelPromise promise) 方法的逻辑

      所以下一篇我们还是回归到前一篇的NioEventLoopGroup这个类的源码学习;把这个类学习完后,我们再学习其它相关联的内容

  相关解决方案