Bootstrap的意思就是引导,辅助的意思,在编写服务端或客户端程序时,我们都需要先new一个bootstrap,然后基于这个bootstrap调用函数,添加eventloop和handler,可见对bootstrap进行分析还是有必要的。
1、bootstrap结构图
bootstrap的结构比较简单,涉及的类和接口很少,如下图所示,其中Bootstrap则是客户端程序用的引导类,ServerBootstrap是服务端程序用的引导类。
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等的处理。