Bootstrap的意思就是引导,辅助的意思,在编写服务端或客户端程序时,我们都需要先new一个bootstrap,然后基于这个bootstrap调用函数,添加eventloop和handler,可见对bootstrap进行分析还是有必要的。

1、bootstrap结构图

bootstrap的结构比较简单,涉及的类和接口很少,如下图所示,其中Bootstrap则是客户端程序用的引导类,ServerBootstrap是服务端程序用的引导类。

netty4.0.x源码分析—bootstrap_java

2、serverbootstrap分析

这部分,专门对serverbootstrap进行分析,bootstrap过程大同小异就不作详细的分析了。下面是我们编写服务端代码的一般化过程,整个分析过程将基于下面这段代码中用到的函数进行。



1. // Configure the bootstrap.  
2. new NioEventLoopGroup();  
3. new NioEventLoopGroup();  
4. try {  
5. new ServerBootstrap();  
6.             b.group(bossGroup, workerGroup)  
7. class)  
8. new HexDumpProxyInitializer(remoteHost, remotePort))  
9. false)  
10.              .bind(localPort).sync().channel().closeFuture().sync();  
11. finally {  
12.             bossGroup.shutdownGracefully();  
13.             workerGroup.shutdownGracefully();  
14.         }


先看关键代码(注意这里面的部分函数是在AbstractBootstrap中定义的)


1. private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();  
2. private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();  
3. private volatile EventLoopGroup childGroup;  
4. private volatile ChannelHandler childHandler;  
5.   
6. /**
7.  * Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
8.  * {@link EventLoopGroup}'s are used to handle all the events and IO for {@link SocketChannel} and
9.  * {@link Channel}'s.
10.  */  
11. public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {  
12. super.group(parentGroup);  
13. if (childGroup == null) {  
14. throw new NullPointerException("childGroup");  
15.     }  
16. if (this.childGroup != null) {  
17. throw new IllegalStateException("childGroup set already");  
18.     }  
19. this.childGroup = childGroup;  
20. return this;  
21. }

属性值ChildGroup,ChildHandler,是用来处理accpt的Channel的。group函数其实就是将parentGroup和ChildGroup进行赋值,其中parentGroup用于处理accept事件,ChildGroup用于处理accpt的Channel的IO事件。



1. //channel函数的实现定义在抽象父类中,其实就是通过newInstance函数生成一个具体的channel对象。  
2. pre name="code" class="java">    /**
3.  * The {@link Class} which is used to create {@link Channel} instances from.
4.  * You either use this or {@link #channelFactory(ChannelFactory)} if your
5.  * {@link Channel} implementation has no no-args constructor.
6.  */  
7. public B channel(Class<? extends C> channelClass) {  
8. if (channelClass == null) {  
9. throw new NullPointerException("channelClass");  
10.     }  
11. return channelFactory(new BootstrapChannelFactory<C>(channelClass));  
12. }  
13.   
14. /**
15.  * {@link ChannelFactory} which is used to create {@link Channel} instances from
16.  * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
17.  * is not working for you because of some more complex needs. If your {@link Channel} implementation
18.  * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for
19.  * simplify your code.
20.  */  
21. @SuppressWarnings("unchecked")  
22. public B channelFactory(ChannelFactory<? extends C> channelFactory) {  
23. if (channelFactory == null) {  
24. throw new NullPointerException("channelFactory");  
25.     }  
26. if (this.channelFactory != null) {  
27. throw new IllegalStateException("channelFactory set already");  
28.     }  
29.   
30. this.channelFactory = channelFactory;  
31. return (B) this;  
32. }<pre name="code" class="java">    private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {  
33. private final Class<? extends T> clazz;  
34.   
35. extends T> clazz) {  
36. this.clazz = clazz;  
37.     }  
38.   
39. @Override  
40. public T newChannel() {  
41. try {  
42. return clazz.newInstance();  
43. catch (Throwable t) {  
44. throw new ChannelException("Unable to create Channel from class " + clazz, t);  
45.         }  
46.     }  
47.   
48. @Override  
49. public String toString() {  
50. return clazz.getSimpleName() + ".class";  
51.     }  
52. }

Channel函数比较简单,其实就是通过newInstance函数,生成一个具体的Channel对象,例如服务端的NioServerSocketChannel。



    1. /**
    2.  * Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
    3.  */  
    4. public ServerBootstrap childHandler(ChannelHandler childHandler) {  
    5. if (childHandler == null) {  
    6. throw new NullPointerException("childHandler");  
    7.     }  
    8. this.childHandler = childHandler;  
    9. return this;  
    10. }


    上面的函数即给serverbootstrap的childHandler赋值。


    1. /**
    2.  * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they get created
    3.  * (after the acceptor accepted the {@link Channel}). Use a value of {@code null} to remove a previous set
    4.  * {@link ChannelOption}.
    5.  */  
    6. public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {  
    7. if (childOption == null) {  
    8. throw new NullPointerException("childOption");  
    9.     }  
    10. if (value == null) {  
    11. synchronized (childOptions) {  
    12.             childOptions.remove(childOption);  
    13.         }  
    14. else {  
    15. synchronized (childOptions) {  
    16.             childOptions.put(childOption, value);  
    17.         }  
    18.     }  
    19. return this;  
    20. }

    上面的函数是指定accpt的channel的属性,channel有很多属性,比如SO_TIMEOUT时间,Buf长度等等。

    1. /**
    2.  * Create a new {@link Channel} and bind it.
    3.  */  
    4. public ChannelFuture bind() {  
    5.     validate();  
    6. this.localAddress;  
    7. if (localAddress == null) {  
    8. throw new IllegalStateException("localAddress not set");  
    9.     }  
    10. return doBind(localAddress);  
    11. }  
    12.   
    13. /**
    14.  * Create a new {@link Channel} and bind it.
    15.  */  
    16. public ChannelFuture bind(int inetPort) {  
    17. return bind(new InetSocketAddress(inetPort));  
    18. }  
    19.   
    20. /**
    21.  * Create a new {@link Channel} and bind it.
    22.  */  
    23. public ChannelFuture bind(String inetHost, int inetPort) {  
    24. return bind(new InetSocketAddress(inetHost, inetPort));  
    25. }  
    26.   
    27. pre name="code" class="java">    /**
    28.  * Create a new {@link Channel} and bind it.
    29.  */  
    30. public ChannelFuture bind(SocketAddress localAddress) {  
    31.     validate();  
    32. if (localAddress == null) {  
    33. throw new NullPointerException("localAddress");  
    34.     }  
    35. return doBind(localAddress);  
    36. }  
    37.   
    38. private ChannelFuture doBind(final SocketAddress localAddress) {  
    39. final ChannelFuture regPromise = initAndRegister();  
    40. final Channel channel = regPromise.channel();  
    41. final ChannelPromise promise = channel.newPromise();  
    42. if (regPromise.isDone()) {  
    43.         doBind0(regPromise, channel, localAddress, promise);  
    44. else {  
    45. new ChannelFutureListener() {  
    46. @Override  
    47. public void operationComplete(ChannelFuture future) throws Exception {  
    48.                 doBind0(future, channel, localAddress, promise);  
    49.             }  
    50.         });  
    51.     }  
    52.   
    53. return promise;  
    54. }<pre name="code" class="java">    private static void doBind0(  
    55. final ChannelFuture regFuture, final Channel channel,  
    56. final SocketAddress localAddress, final ChannelPromise promise) {  
    57.   
    58. // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up  
    59. // the pipeline in its channelRegistered() implementation.  
    60.   
    61. new Runnable() {  
    62. @Override  
    63. public void run() {  
    64. if (regFuture.isSuccess()) {  
    65.                 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);  
    66. else {  
    67.                 promise.setFailure(regFuture.cause());  
    68.             }  
    69.         }  
    70.     });  
    71. }


    Bind函数层层调用过来之后,最后就调用Channel的bind函数了,下面再看channel的bind函数是如何处理的。定义在AbstractChannel中:

    1. @Override  
    2. public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {  
    3. return pipeline.bind(localAddress, promise);  
    4. }


    channel的bind函数,最终就是调用pipeline的bind,而pipeline的bind实际上就是调用contexthandler的bind,之个之前分析write和flush的时候说过了。所以这里直接看contexthandler的bind函数。下面是定义:


    1. @Override  
    2. public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {  
    3. if (localAddress == null) {  
    4. throw new NullPointerException("localAddress");  
    5.     }  
    6. false);  
    7.   
    8. final DefaultChannelHandlerContext next = findContextOutbound();  
    9.     EventExecutor executor = next.executor();  
    10. if (executor.inEventLoop()) {  
    11.         next.invokeBind(localAddress, promise);  
    12. else {  
    13. new Runnable() {  
    14. @Override  
    15. public void run() {  
    16.                 next.invokeBind(localAddress, promise);  
    17.             }  
    18.         });  
    19.     }  
    20.   
    21. return promise;  
    22. }<pre name="code" class="java">    private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {  
    23. try {  
    24. this, localAddress, promise);  
    25. catch (Throwable t) {  
    26.         notifyOutboundHandlerException(t, promise);  
    27.     }  
    28. }

    最终调用Handler的bind函数,还记得之前说的outbound类型的事件吗,这类事件提供了默认的实现方法,HeadHandler的bind函数,下面是它的定义:


    1. @Override  
    2. public void bind(  
    3.         ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)  
    4. throws Exception {  
    5.     unsafe.bind(localAddress, promise);  
    6. }


    我们又看到了unsafe这个苦力了,最终的操作还是得由它来完成啊,赶紧去看看这个bind函数吧,



    1. @Override  
    2. public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {  
    3. if (!ensureOpen(promise)) {  
    4. return;  
    5.     }  
    6.   
    7. // See: https://github.com/netty/netty/issues/576  
    8. if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() &&  
    9.         Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&  
    10. instanceof InetSocketAddress &&  
    11.         !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress()) {  
    12. // Warn a user about the fact that a non-root user can't receive a  
    13. // broadcast packet on *nix if the socket is bound on non-wildcard address.  
    14.         logger.warn(  
    15. "A non-root user can't receive a broadcast packet if the socket " +  
    16. "is not bound to a wildcard address; binding to a non-wildcard " +  
    17. "address (" + localAddress + ") anyway as requested.");  
    18.     }  
    19.   
    20. boolean wasActive = isActive();  
    21. try {  
    22.         doBind(localAddress);  
    23. catch (Throwable t) {  
    24.         closeIfClosed();  
    25.         promise.setFailure(t);  
    26. return;  
    27.     }  
    28. if (!wasActive && isActive()) {  
    29. new Runnable() {  
    30. @Override  
    31. public void run() {  
    32.                 pipeline.fireChannelActive();  
    33.             }  
    34.         });  
    35.     }  
    36.     promise.setSuccess();  
    37. }

    上面的代码最终调用了Channel的doBind函数,这里我们的Channel是NioServerSocketChannel,所以最终就是调用它的bind函数了,代码如下


    1. @Override  
    2. protected void doBind(SocketAddress localAddress) throws Exception {  
    3.     javaChannel().socket().bind(localAddress, config.getBacklog());  
    4. }

    其实它最终也是调用了JDK的Channel的socket bind函数。


    看到这里,你是否会觉得有点怪异,为什么没有注册accpt事件啊,一般的我们的server socket都是要注册accpt事件到selector,用于监听连接。如果你发现了这个问题,说明你是理解socket的编程的,^_^。实际上是前面在分析bind的时候我们漏掉了一个重要的函数,initAndRegister,下面再来看看它的定义:



    1. final ChannelFuture initAndRegister() {  
    2. final Channel channel = channelFactory().newChannel();  
    3. try {  
    4.         init(channel);  
    5. catch (Throwable t) {  
    6.         channel.unsafe().closeForcibly();  
    7. return channel.newFailedFuture(t);  
    8.     }  
    9.   
    10.     ChannelPromise regPromise = channel.newPromise();  
    11.     group().register(channel, regPromise);  
    12. if (regPromise.cause() != null) {  
    13. if (channel.isRegistered()) {  
    14.             channel.close();  
    15. else {  
    16.             channel.unsafe().closeForcibly();  
    17.         }  
    18.     }  
    19.   
    20. // If we are here and the promise is not failed, it's one of the following cases:  
    21. // 1) If we attempted registration from the event loop, the registration has been completed at this point.  
    22. //    i.e. It's safe to attempt bind() or connect() now beause the channel has been registered.  
    23. // 2) If we attempted registration from the other thread, the registration request has been successfully  
    24. //    added to the event loop's task queue for later execution.  
    25. //    i.e. It's safe to attempt bind() or connect() now:  
    26. //         because bind() or connect() will be executed *after* the scheduled registration task is executed  
    27. //         because register(), bind(), and connect() are all bound to the same thread.  
    28.   
    29. return regPromise;  
    30. }


    在这里,我们看到了我们之前介绍event时说的register函数,它就是用于将Channel注册到eventloop中去的。eventloop经过层层调用,最终调用了SingleThreadEventLoop类中的register函数,下面是它的定义:



    1. @Override  
    2. public ChannelFuture register(final Channel channel, final ChannelPromise promise) {  
    3. if (channel == null) {  
    4. throw new NullPointerException("channel");  
    5.     }  
    6. if (promise == null) {  
    7. throw new NullPointerException("promise");  
    8.     }  
    9.   
    10. this, promise);  
    11. return promise;  
    12. }


    还是逃离不了unsafe对象的调用,前面说了那么多的unsafe,这个函数猜都可以猜测出执行过程了,这里就不细细的列举代码了。


    还有一个init函数,这里需要说明一下,代码如下:



    1. @Override  
    2. void init(Channel channel) throws Exception {  
    3. final Map<ChannelOption<?>, Object> options = options();  
    4. synchronized (options) {  
    5.         channel.config().setOptions(options);  
    6.     }  
    7.   
    8. final Map<AttributeKey<?>, Object> attrs = attrs();  
    9. synchronized (attrs) {  
    10. for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {  
    11. @SuppressWarnings("unchecked")  
    12.             AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();  
    13.             channel.attr(key).set(e.getValue());  
    14.         }  
    15.     }  
    16.   
    17.     ChannelPipeline p = channel.pipeline();  
    18. if (handler() != null) {  
    19.         p.addLast(handler());  
    20.     }  
    21.   
    22. final EventLoopGroup currentChildGroup = childGroup;  
    23. final ChannelHandler currentChildHandler = childHandler;  
    24. final Entry<ChannelOption<?>, Object>[] currentChildOptions;  
    25. final Entry<AttributeKey<?>, Object>[] currentChildAttrs;  
    26. synchronized (childOptions) {  
    27.         currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));  
    28.     }  
    29. synchronized (childAttrs) {  
    30.         currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));  
    31.     }  
    32.   
    33. new ChannelInitializer<Channel>() {  
    34. @Override  
    35. public void initChannel(Channel ch) throws Exception {  
    36. new ServerBootstrapAcceptor(  
    37.                     currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  
    38.         }  
    39.     });  
    40. }


    它就是用来处理channel 的pipeline,并添加一个ServerBootstrapAcceptor的handler,继续看看这个handler的定义,我们就会明白它的意图。



      1. private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {  
      2.   
      3. private final EventLoopGroup childGroup;  
      4. private final ChannelHandler childHandler;  
      5. private final Entry<ChannelOption<?>, Object>[] childOptions;  
      6. private final Entry<AttributeKey<?>, Object>[] childAttrs;  
      7.   
      8. @SuppressWarnings("unchecked")  
      9.     ServerBootstrapAcceptor(  
      10.             EventLoopGroup childGroup, ChannelHandler childHandler,  
      11.             Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {  
      12. this.childGroup = childGroup;  
      13. this.childHandler = childHandler;  
      14. this.childOptions = childOptions;  
      15. this.childAttrs = childAttrs;  
      16.     }  
      17.   
      18. @Override  
      19. @SuppressWarnings("unchecked")  
      20. public void channelRead(ChannelHandlerContext ctx, Object msg) {  
      21.         Channel child = (Channel) msg;  
      22.   
      23.         child.pipeline().addLast(childHandler);  
      24.   
      25. for (Entry<ChannelOption<?>, Object> e: childOptions) {  
      26. try {  
      27. if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {  
      28. "Unknown channel option: " + e);  
      29.                 }  
      30. catch (Throwable t) {  
      31. "Failed to set a channel option: " + child, t);  
      32.             }  
      33.         }  
      34.   
      35. for (Entry<AttributeKey<?>, Object> e: childAttrs) {  
      36.             child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());  
      37.         }  
      38.   
      39. try {  
      40.             childGroup.register(child);  
      41. catch (Throwable t) {  
      42.             child.unsafe().closeForcibly();  
      43. "Failed to register an accepted channel: " + child, t);  
      44.         }  
      45.     }  
      46.   
      47. @Override  
      48. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
      49. final ChannelConfig config = ctx.channel().config();  
      50. if (config.isAutoRead()) {  
      51. // stop accept new connections for 1 second to allow the channel to recover  
      52. // See https://github.com/netty/netty/issues/1328  
      53. false);  
      54. new Runnable() {  
      55. @Override  
      56. public void run() {  
      57. true);  
      58.                 }  
      59. 1, TimeUnit.SECONDS);  
      60.         }  
      61. // still let the exceptionCaught event flow through the pipeline to give the user  
      62. // a chance to do something with it  
      63.         ctx.fireExceptionCaught(cause);  
      64.     }  
      65. }


      上面就是这个handler的全部代码,它重写了ChannelRead函数,它的目的其实是想将server端accept的channel注册到ChildGroup的eventloop中,这样就可以理解,服务端代码workerGroup这个eventloop的作用了,它终于在这里体现出了它的作用了。


      3、总结

      这篇文章主要是分析了serverbootstrap的全过程,通过对这个的分析,我们清晰的看到了平时编写socket服务端代码时对bind,register事件,以及accept channel等的处理。