Netty 中 EventLoopGroup 的创建

时间:2022-09-26 23:16:28

  本文是基于 Netty 4.1.6.Final 的源码来分析的。

  在分析源码之前做一些准备工作:

  先熟悉一下 IDEA 的几个快捷键,能极大的提高我们查看源码的效率:

  1. Ctrl + Alt + B:用鼠标点击指定的方法,然后按下快捷键,IDEA 就会跳转到该方法的定义的地方,如果是重写的方法,则会列出该方法的所有实现;
  2. Ctrl + Alt + ←/→:跳转至前/后一次鼠标点击的地方,方便我们来回查看源码;
  3. Ctrl + F12:弹出当前类的所有方法,可以直接敲字母来过滤方法;
  4. Shift + F7:Debug 的时候,当一行代码中链式的调用了多个方法,按下该快捷键会弹出改行所有的方法,然后选择要进入的方法,查看源码。

1. 创建过程

  1. 创建 1 个 executor,后续用来创建并执行线程;
  2. 创建指定数量的 EventLoop;
    1. 为当前 EventLoop 创建 1 个 selector;
  3. 根据 EventLoop 的数量创建指定类型的 chooser,后续用来分配线程。

2. 代码

  EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

  这两行代码创建的 EventLoopGroup 分别用来处理新连接的接入和已接入连接的事件处理。

3. 源码分析

   NioEventLoopGroup 的构造方法,最终调用的是 MultithreadEventExecutorGroup 的构造方法:

 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
//...
//1. 创建 executor
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// EventLoopGroup 中的 EventLoop 数组
children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//2. 创建 EventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
//...
}
}
//3. 创建 chooser
chooser = chooserFactory.newChooser(children);
//..
}

3.1 executor 的创建

  创建 executor 的构造方法中传入了 1 个 DefaultThreadFactory:

 public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
//... poolName 的值是 EventLoopGroup 的类名,首字母小写
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
this.daemon = daemon;
this.priority = priority;
this.threadGroup = threadGroup;
}

  ThreadPerTaskExecutor  类:

 public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory; public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
} @Override
public void execute(Runnable command) {
//通过线程工厂创建并启动线程
threadFactory.newThread(command).start();
}
}

  DefaultThreadFactory 的 newThread(Runnable command)方法:

 @Override
public Thread newThread(Runnable r) {
//调用了后面的方法,最终创建的是 Netty 封装的 FastThreadLocalThread
Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
try {//线程相关的设置
if (t.isDaemon()) {
if (!daemon) {
t.setDaemon(false);
}
} else {
if (daemon) {
t.setDaemon(true);
}
} if (t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}

  注意:这里只是分析了 executor 的创建,以及它创建线程的方法,这一阶段并没有创建和运行新线程。

3.2 EventLoop 的创建

  newChild()方法将 executor 传了进去,这里以 NioEventLoop 举例,所以最终调用了 NioEventLoop 的构造方法:

 NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
//executor 最终传递给父类 SingleThreadEventExecutor
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
//创建 selector
selector = openSelector();
selectStrategy = strategy;
}

  在 NioEventLoop 几个父类的构造方法中,创建了任务队列,暂时不做分析。

3.3 chooser 的创建

  chooser 也是通过工厂模式创建的,参数 children 是前面创建的 EventLoop 数组,chooserFactory 会根据数组的长度是否为 2 的幂来创建 chooser。

 @SuppressWarnings("unchecked")
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTowEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
} private static boolean isPowerOfTwo(int val) {
//判断数组长度是否为 2 的幂
//有符号数的计算:以 Byte 为例
// 3 ---> 0000 0011 0000 0011
// -3 ---> 1000 0000 - 0000 0011 = 0111 1101 ===> 1111 1101 &
// 0000 0000
// 2 ---> 0000 0010 0000 0010
// -2 ---> 1000 0000 - 0000 0010 = 0111 1110 ===> 1111 1110 &
// 0000 0010
return (val & -val) == val;
} private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors; PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
} @Override
public EventExecutor next() {
//因为 length 是 2 的幂,减去 1,退一位,二进制就全是 1
//比如 8 是 1000,减 1 是 0111,将 idx 自增后和前面的值相与
//相当于是循环取值
return executors[idx.getAndIncrement() & executors.length - 1];
}
} private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
} @Override
public EventExecutor next() {
//普通的就是直接取模的绝对值
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}

  至此,EventLoopGroup 就创建完成了,boosGroup 和 wrokerGroup 的创建是一样的。