netty4 bind啓動過程簡析

請看一下簡單的 一個netty4服務端啓動代碼樣例java

 EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
            b.handler(new LoggingHandler(LogLevel.DEBUG));
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new echoServerHandler());
                }
            });
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.childOption(ChannelOption.SO_BACKLOG, 128);

            ChannelFuture channelFuture = b.bind(2000).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            System.out.println("called shutdown gracefully...");
        }

你們看到了,git

 ChannelFuture channelFuture = b.bind(2000).sync();發生了什麼調用呢 ?下面分析一下其調用鏈.首先上一個調用的輪廓圖


上圖是 netty4 bind()執行的時候的調用鏈.

下面看代碼層面的調用 :

1.AbstractBootstrap
樣例建立的ServerBootstrap 執行bind的時候,直接執行父類的bind() ,它自己沒有本身的實現.
//AbstractBootstrap  

  public ChannelFuture bind() {
        validate();
        SocketAddress localAddress = this.localAddress;
        if (localAddress == null) {
            throw new IllegalStateException("localAddress not set");
        }
        return doBind(localAddress);
    }



//--------------------------
 private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regPromise = initAndRegister();
        final Channel channel = regPromise.channel();
        final ChannelPromise promise = channel.newPromise();
        if (regPromise.isDone()) {
            doBind0(regPromise, channel, localAddress, promise);
        } else {
            regPromise.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    doBind0(future, channel, localAddress, promise);
                }
            });
        }

        return promise;
    }



//-------------------3--------------------------
 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() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

2.bootStrap的bind會調用大abstractChannel的bind方法github

//AbstractChannel

//- 
  @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }

3.abstractChannel會去調用 DefaultChannelPipeline的bindpromise

///DefaultChannelPipeline

 @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }

4.DefaultChannelPipeline就是channel的管道.由於bind屬於out方法,所以調用管道順序是 tail ->head (handler)socket

  在調用tail的bind的時候 ,走到了 DefaultChannelHandlerContext的bind,請看ide

//DefaultChannelHandlerContext
  @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        validatePromise(promise, false);

        final DefaultChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            });
        }

        return promise;
    }

5.上面有一句 final DefaultChannelHandlerContext next = findContextOutbound(); 要找outbound事件執行bindoop

只有headHandler繼承了outBoundHandler,這時候又走到了DefaultChannelHandlerContext 的 invokeBindthis

// DefaultChannelHandlerContext 
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) { try { ((ChannelOutboundHandler) handler).bind(this, localAddress, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } }

6.此時走到了 headhandler 的bind裏面去了.(此時handler只有 tailHandler,headHeadler,ServerBootstrapAcceptor等幾個.只有head屬於outboundler)spa

//HeadHandler
   @Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }

7.此時調用鏈走到了unsafe的bind裏面.AbstractUnsafe代碼調用邏輯以下netty

  @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            if (!ensureOpen(promise)) {
                return;
            }

            // See: https://github.com/netty/netty/issues/576
            if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() &&
                Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress()) {
                // 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) {
                closeIfClosed();
                promise.setFailure(t);
                return;
            }
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }
            promise.setSuccess();
        }

8.unsafe屬於AbstractChannel的內部類.所以doBind(localAddress)調用的就是AbstractChannel的子類NioServerSocketChannel的方法

//NioServerSocketChannel
 @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }

//
@Override
protected ServerSocketChannel javaChannel() {
return (ServerSocketChannel) super.javaChannel();
}

//abstractNioChannel 能夠看到 這個channle的來源是 java.nio.serverSocketChannel

protected SelectableChannel javaChannel() {
return ch;
}
 

 

此動做完成了 .bind方法的的全部bind操做.
相關文章
相關標籤/搜索