請看一下簡單的 一個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();發生了什麼調用呢 ?下面分析一下其調用鏈.首先上一個調用的輪廓圖
![](http://static.javashuo.com/static/loading.gif)
上圖是 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操做.