Netty 源码跟踪(一)-- netty启动

1. 概述

使用2019.11.6日4.1版本最新源码。

源码下载:
链接:https://pan.baidu.com/s/19R-rtyT0ffyv3NAOrZKJ5Q 密码:5xhp

为了方便理解,文中代码做了简化。

2. 服务端创建

代码示例来自netty源码io.netty.example.echo包中EchoServer类

EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory());
EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory());
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
  ServerBootstrap b = new ServerBootstrap();
  b.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .option(ChannelOption.SO_BACKLOG, 100)
    .handler(new LoggingHandler(LogLevel.INFO))
    .childHandler(new ChannelInitializer<SocketChannel>() {
      @Override
      public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline p = ch.pipeline();
        if (sslCtx != null) {
          p.addLast(sslCtx.newHandler(ch.alloc()));
        }
        p.addLast(serverHandler);
      }
    });

  ChannelFuture f = b.bind(PORT).sync();

  f.channel().closeFuture().sync();
} finally {
  bossGroup.shutdownGracefully();
  workerGroup.shutdownGracefully();
}

2.1 创建EventLoopGroup

我们直接看workerGroup的创建,因为bossGroup和workerGroup区别就在于,线程数量不同。在构造函数中,我们发现,调用了pickThreadCount(nThreads)这个方法。

protected MultithreadEventLoopGroup(int nThreads, Executor executor,
                 IoHandlerFactory ioHandlerFactory,
                 int maxPendingTasks, RejectedExecutionHandler rejectedHandler,
                 int maxTasksPerRun, Object... args) {
        super(
        pickThreadCount(nThreads),
        executor == null ? new ThreadPerTaskExecutor(newDefaultThreadFactory()) : executor,
        maxPendingTasks, rejectedHandler, merge(ioHandlerFactory, maxTasksPerRun, args));
}
2.1.1 选择线程数量

pickThreadCount()的函数定义如下。它是MultithreadEventLoopGroup的方法。worker EventLoopGroup传入了初始化线程数量,nThreads为传入的值,boss EventLoopGroup调用时默认传入0,那么nThreads的值为DEFAULT_EVENT_LOOP_THREADS常量的值。

protected static int pickThreadCount(int nThreads) {
  			// 如果nThreads是0,就更改为DEFAULT_EVENT_LOOP_THREADS常量值
        return nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads;
}

我们发现MultithreadEventLoopGroup的静态代码块中初始化了常量DEFAULT_EVENT_LOOP_THREADS,最小是1,否则设置为当前电脑可用核心数的2倍。

static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

        if (logger.isDebugEnabled()) {
            logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
        }
}

此时调动栈如下。

在这里插入图片描述

2.1.2 创建selector

往后面看,最后实现创建的构造函数如下

protected MultithreadEventExecutorGroup(
  int nThreads, Executor executor, 
  int maxPendingTasks, RejectedExecutionHandler rejectedHandler, Object... args) {
				// nThreads 为刚才提到的线程数量,此处使用一个数组保存EventLoop
        children = new EventExecutor[nThreads];
        powerOfTwo = isPowerOfTwo(children.length);
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(executor, maxPendingTasks, rejectedHandler, args);
                success = true;
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a child event executor", e);
            } finally {
                // 省略...
            }
        }
    }

在newChild函数中,创建了NioHandler

@Override
protected final EventLoop newChild(
  Executor executor, int maxPendingTasks,
  RejectedExecutionHandler rejectedExecutionHandler, Object... args) {
  return newChild(executor, maxPendingTasks, rejectedExecutionHandler,
             ((IoHandlerFactory) args[0]).newHandler()/*创建NioHandler*/, (Integer) args[1],
             Arrays.copyOfRange(args, 2, args.length));
}

在NioHandler的构造函数中,创建了selector

private NioHandler(SelectorProvider selectorProvider, SelectStrategy strategy) {
        provider = selectorProvider;
  			// 创建selector
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

至此,函数调用栈如下:

在这里插入图片描述

总结一下,创建MultithreadEventLoopGroup有两件核心的事情。第一是,如果参数传入了线程数,就用传入的值,如果没有,就会使用电脑可用核心数的2倍作为线程数。第二个,selector是在MultithreadEventLoopGroup创建的时候创建的。

2.2 创建ServerBootstrap

创建ServerBootstrap,使用Builder模式。

首先看group方法。将两个EventLoopGroup分别保存。

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
  	// AbstractBootstrap类
    super.group(parentGroup);
    requireNonNull(childGroup, "childGroup");
    if (this.childGroup != null) {
      throw new IllegalStateException("childGroup set already");
    }
    this.childGroup = childGroup;
    return this;
}

下面是channel方法。使用反射创建ServerChannel。

public ServerBootstrap channel(Class<? extends ServerChannel> channelClass) {
    requireNonNull(channelClass, "channelClass");
    return channelFactory(new ReflectiveServerChannelFactory<ServerChannel>(channelClass));
}

public ReflectiveServerChannelFactory(Class<? extends T> clazz) {
    requireNonNull(clazz, "clazz");
    try {
      // 创建constructor
      this.constructor = clazz.getConstructor(EventLoop.class, EventLoopGroup.class);
    } catch (NoSuchMethodException e) {
      throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) + 
 " does not have a public constructor that takes an EventLoop and EventLoopGroup instance", e);
    }
}

@Override
public T newChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
    try {
      // 生成实例
      return constructor.newInstance(eventLoop, childEventLoopGroup);
    } catch (Throwable t) {
      throw new ChannelException(
        "Unable to create ServerChannel from class " + constructor.getDeclaringClass(), t);
    }
}

添加option,添加handler,添加childrenHandler。

public <T> B option(ChannelOption<T> option, T value) {
    requireNonNull(option, "option");
    if (value == null) {
        options.remove(option);
    } else {
        options.put(option, value);
    }
    return self();
}

public B handler(ChannelHandler handler) {
    requireNonNull(handler, "handler");
    this.handler = handler;
    return self();
}

public ServerBootstrap childHandler(ChannelHandler childHandler) {
    requireNonNull(childHandler, "childHandler");
    this.childHandler = childHandler;
    return this;
}

2.3 绑定方法

之后执行这一句:

 ChannelFuture f = b.bind(PORT).sync();

bind函数实际执行的是doBind函数,如下。

private ChannelFuture doBind(final SocketAddress localAddress) {
  			// 1. 首先创建并初始化channel
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }
        if (regFuture.isDone()) {
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            final ChannelPromise promise = channel.newPromise();
            regFuture.addListener((ChannelFutureListener) future -> {
                Throwable cause = future.cause();
                if (cause != null) {
                    promise.setFailure(cause);
                } else {
                  	// 注意这里,后面boss EventLoop线程会执行到这里。
                    doBind0(regFuture, channel, localAddress, promise);
                }
            });
            return promise;
        }
    }
2.3.1 创建channel

我们先看一下initAndRegister函数,创建channel都有哪些内容。首先获取boss EventLoop线程,然后使用反射创建channel,最后使用boss EventLoop线程初始化channel。

final ChannelFuture initAndRegister() {
  	// loop是boss EventLoop
    EventLoop loop = group.next();
    final Channel channel;
    try {
      // 创建channel,这里使用了反射
      channel = newChannel(loop);
    } catch (Throwable t) {
      return new FailedChannel(loop).newFailedFuture(t);
    }

    final ChannelPromise promise = channel.newPromise();
  	// 这里初始化channel,boss EventLoop线程来执行,会添加Handler到pipline中
    loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {
      if (future.isSuccess()) {
        channel.register(promise);
      } else {
        channel.unsafe().closeForcibly();
        promise.setFailure(future.cause());
      }
    }));

    return promise;
}

到此为止,我们已经完成了channel的创建,并且,让boss EventLoop线程完成了channel的初始化,main线程返回当前函数,返回regFuture,由于regFuture的任务并没有完成,所以,main函数注册了回调方法,执行doBind0函数,此时main函数返回 ChannelFuture f = b.bind(PORT).sync();执行sync,main线程就阻塞等待了。

2.3.2 初始化channel

在我们把目光转移到boss EventLoop (后面简称boss线程),刚才我们说到boss线程是在AbstractBootsrap类的initAndRegister方法中开启的。首先要完成channel的初始化工作。

loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {...}));

调用下面的初始化方法。

ChannelFuture init(Channel channel) {
    final ChannelPromise promise = channel.newPromise();
    setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger);
    setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0)));

    ChannelPipeline p = channel.pipeline();

    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions =
      													childOptions.entrySet().toArray(newOptionArray(0));
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs = 		
      													childAttrs.entrySet().toArray(newAttrArray(0));

    p.addLast(new ChannelInitializer<Channel>() {
      @Override
      public void initChannel(final Channel ch) {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = config.handler();
        if (handler != null) {
          pipeline.addLast(handler);
        }

        ch.eventLoop().execute(() -> {
          pipeline.addLast(new ServerBootstrapAcceptor(
            ch, currentChildHandler, currentChildOptions, currentChildAttrs));
          promise.setSuccess();
        });
      }
    });
    return promise;
}

我们看ChannelPipline的addLast方法,参数是可变参数的ChannelHandler
函数签名 : public final ChannelPipeline addLast(ChannelHandler… handlers)
ChannelInitializer 实现了 ChannelInboundHandler 接口 , ChannelInboundHandler 继承了 ChannelHandler

下面的代码中,调用了另一个addLast方法,虽然方法里面有循环,但是经过查看运行时数据,我们发现,参数handlers只有一个变量,ServerBootstrap类型。

@Override
    public final ChannelPipeline addLast(ChannelHandler... handlers) {
        requireNonNull(handlers, "handlers");

        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(null, h);
        }

        return this;
}

经过下图中的调用栈,最后执行initChannel方法,也就是初始化开始ServerBootstrap类的init方法中,匿名内部类的方法,如下,将handler添加到pipeline中。

在这里插入图片描述

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) {
      final ChannelPipeline pipeline = ch.pipeline();
      ChannelHandler handler = config.handler();
      if (handler != null) {
        pipeline.addLast(handler);
      }

      ch.eventLoop().execute(() -> {
        pipeline.addLast(new ServerBootstrapAcceptor(
          ch, currentChildHandler, currentChildOptions, currentChildAttrs));
        promise.setSuccess();
      });
    }
});

特别要说的是,在这之前,ServerBootStrap自己也在pipeline中,在调用栈的handlerAdded方法中,完成了其他handler的添加,才将自己删除,代码如下。

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    try {
      initChannel((C) ctx.channel());
    } catch (Throwable cause) {
      exceptionCaught(ctx, cause);
    } finally {
      ChannelPipeline pipeline = ctx.pipeline();
      if (pipeline.context(this) != null) {
        // 删除自己
        pipeline.remove(this);
      }
    }
}

删除之前,ServerBootstrap也在pipeline的handlers链中。

在这里插入图片描述

移除自己。

在这里插入图片描述

2.3.3 注册channel到selector

本以为到这里就结束了,但是我们在唤醒boss线程时,还注册了回调函数。

loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {
    if (future.isSuccess()) {
      // 执行这里
      channel.register(promise);
    } else {
      channel.unsafe().closeForcibly();
      promise.setFailure(future.cause());
    }
}));

通过下面的调用栈,我们调用到了NioHandler的register方法。我们可以看到,方法中调用了java NIO中的register方法,真正的注册方法。

在这里插入图片描述

@Override
public void register(Channel channel) throws Exception {
    AbstractNioChannel nioChannel = cast(channel);
    boolean selected = false;
    for (;;) {
        try {
          	// 真正的注册方法,要注意,此时监听的是0,而不是ACCEPT。
            nioChannel.selectionKey = nioChannel.javaChannel().register(unwrappedSelector(), 0, nioChannel);
            return;
        } catch (CancelledKeyException e) {
					// 省略...
        }
    }
}
2.3.4 真正的绑定方法

main线程还在doBind方法中注册了一个回调函数。

private ChannelFuture doBind(final SocketAddress localAddress) {
		// 省略...
    if (regFuture.isDone()) {
		// ...
    } else {
      final ChannelPromise promise = channel.newPromise();
      // 回调函数
      regFuture.addListener((ChannelFutureListener) future -> {
        Throwable cause = future.cause();
        if (cause != null) {
          promise.setFailure(cause);
        } else {
          // 执行这里
          doBind0(regFuture, channel, localAddress, promise);
        }
      });
      return promise;
    }
}

在doBind0方法中,执行了channel的bind方法。

private static void doBind0(
    final ChannelFuture regFuture, final Channel channel,
    final SocketAddress localAddress, final ChannelPromise promise) {
    channel.eventLoop().execute(() -> {
      if (regFuture.isSuccess()) {
        channel.bind(localAddress, promise)
          			.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
      } else {
        promise.setFailure(regFuture.cause());
      }
    });
}

随着下面的调用栈,我们找到NioServerSocketChannel类的doBind方法。

在这里插入图片描述
这里调用了Java NIO的bind方法。

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

下面我们往回走,回到调用栈的第二行,AbstractChannel类中的bind方法。

@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
		// 省略...
    boolean wasActive = isActive();
    try {
      	// 从这里返回
        doBind(localAddress);
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        closeIfClosed();
        return;
    }

    if (!wasActive && isActive()) {
        invokeLater(() -> {
            pipeline.fireChannelActive();
            // 看这里
            readIfIsAutoRead();
        });
    }

    safeSetSuccess(promise);
}

我们跟随以下调用栈进入readIfIsAutoRead方法,最后来到AbstractNioChannel类的doBeginRead方法。

在这里插入图片描述

@Override
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        // 这里注册了创建连接事件
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

到此,netty就启动起来了。

总结一下,首先创建EventLoopGroup,其中选择线程数量,创建了selector。之后创建ServerBootstrap,使用Builder的建造模式设置属性值。最后执行绑定方法,创建并初始化channel,将channel注册到selector,但注意这里监听的是0,执行真正的绑定方法,并监听ACCEPT事件。

博主水平有限,文中难免出现错误,希望大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值