首页 > Web开发 > 详细

netty4 bind启动过程简析

时间:2016-01-07 00:54:59      阅读:472      评论:0      收藏:0      [点我收藏+]

请看一下简单的 一个netty4服务端启动代码样例

 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...");
        }

大家看到了,

 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方法

//AbstractChannel

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

3.abstractChannel会去调用 DefaultChannelPipeline的bind

///DefaultChannelPipeline

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

4.DefaultChannelPipeline就是channel的管道.因为bind属于out方法,因此调用管道顺序是 tail ->head (handler)

  在调用tail的bind的时候 ,走到了 DefaultChannelHandlerContext的bind,请看

//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事件执行bind

只有headHandler继承了outBoundHandler,这时候又走到了DefaultChannelHandlerContext 的 invokeBind

// 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)

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

7.此时调用链走到了unsafe的bind里面.AbstractUnsafe代码调用逻辑如下

  @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操作.


netty4 bind启动过程简析

原文:http://www.cnblogs.com/wangkaick/p/5107818.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!