EventLoopGroup整体
- EventLoop,字面意思来看是事件循环,内部是一个线程;Netty 是Reactor线程模型的一种实现,结合Netty 中的线程模型和 Reactor 模型来理解EventLoop 在Netty 中的作用;
- EventLoopGroup,是一组EventLoop,内部有一组EventLoop 线程
- 本文先从整体来看看:EventLoopGroup
一、EventLoopGroup和Reactor线程模型
1.1 Reactor 线程模型
- 单线程模型:接受连接和IO处理在一个线程
- 多线程模型:接受连接和IO处理在不同线程,一个线程负责接受连接,另外一个线程池负责IO处理
- 主从模型:一个线程池负责接受连接,另一个线程池负责IO处理
1.2 EventLoopGroup对模型实现
- Netty server端的三种Reactor模型实现:
- 单线程模型
单线程模型:
EventLoopGroup single = new NioEventLoopGroup(1);
bootstrap.group(single);
- 多线程模型
多线程模型:
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();
bootstrap.group(boss, worker);
- 主从模型
主从线程模型:
NioEventLoopGroup boss = new NioEventLoopGroup(4);
NioEventLoopGroup worker = new NioEventLoopGroup();
bootstrap.group(boss, worker);
-
需要注意的是:Netty 中没有对主从模型的实现,在这段主从的代码中,其实boss NioEventLoopGroup里面的四个线程只有第一个会监听指定端口,等价于多线程模型;
二、上层类和接口
-
我们先看看整体的继承关系,其中 NioEventLoop和 NioEventLoopGroup 是最底层的子类,从顶层来看,EventExecutorGroup 是前面两个类共同的父接口,并且 EventExecutorGroup 是继承了JDK中的 ScheduledExecutorService接口,ScheduledExecutorService 表示具备周期性执行或者一段时间后延迟执行的线程池,JDK基本线程池之一的 ScheduledThreadPoolExecutor 就是它的实现类,由此我们可以基本的知道 EventLoop和EventLoopGroup 本质上都是一种线程池,具备线程池的执行任务的功能和特性。
-
EventLoopGroup主要包含2个方面的功能:注册 Channel和执行一些 Runnable任务。
-
EventLoop 内部对应着一个线程,且整个生命周期中对应的线程不会发生改变,
-
我们先看看接口大致功能,再从 EventLoopGroup 的功能入手去分析,下面是整个继承体系;
2.1 EventExecutorGroup
- 前面提到了 EventExecutorGroup 是很关键的一个接口,它继承了JDK的原生线程池接口,同时又是 EventLoop和EventLoopGroup 的公共父接口,它也定义了一系列的接口方法,对于继承自 Executor、ExecutorService和ScheduledExecutorService 里面的方法我们不做关注,有兴趣可以参阅线程池部分的源码。
/**
* EventExecutor(事件执行器)分组接口,提供处理EventExecutor生命周期的方法,
* 比如关闭方法shutdownGracefully、terminationFuture(全部EventExecutor关闭之后会调用对应的Future)
* isShuttingDown:判断全部的EventExecutor是否都已经关闭
* 另外提供了next方法用于返回一个EventExecutor
* 另外他重写了继承自父类的一些方法,默认也是空实现,避免子类需要实现全部的方法
*/
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor> {
boolean isShuttingDown();
Future<?> shutdownGracefully();
Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit);
Future<?> terminationFuture();
/**
*这个方法很关键没返回内部管理的一个 EventExecutor 对象,用于执行任务
* Returns one of the {@link EventExecutor}s managed by this {@link EventExecutorGroup}.
*/
EventExecutor next();
//下面的@Override方法都是继承自父类的方法
@Override
Iterator<EventExecutor> iterator();
@Override
Future<?> submit(Runnable task);
@Override
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
@Override
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
@Override
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
}
- 可以看到 EventExecutorGroup 内部定义从线程池接口继承了很多执行定时任务和提交任务的方法,很多异步的方法返回的是 Future 对象,不过这个 Future 对象是Netty 框架自身定义的,而非JDK 的
- 这里需要注意的是 next(); 方法,它用于返回一个 EventExecutor 事件执行器,后面在子类中可以看到对于submit和schedule 等方法的实现就是通过next 获取到执行器后执行的,这里的next 在后面可以看到是通过策略 EventExecutorChooser 来选择的
- 下面我们都分析 EventExecutorGroup 的子类和派生子类
2.2 AbstractEventExecutorGroup
- AbstractEventExecutorGroup 实现了 EventExecutorGroup,将其中的执行任务方法逻辑做了初步实现。
- 大致方法包括:
1.submit提交任务,并选择一个 EventExecutor 执行
2.schedule 提交定时任务,并选择一个 EventExecutor 执行
3.invokeAll:调用全部方法,并选择一个 EventExecutor 执行 (继承自线程池的方法)
4.invokeAny:调用任意,并选择一个 EventExecutor 执行 (继承自线程池的方法)
5.execute:执行普通任务
6.shutdown / shutdownGracefully:关闭
- 部分方法如下:
/**
* EventExecutorGroup的抽象实现类
*
* 里面的实现基本都是基于 EventExecutorGroup 的一个 next 方法来做的,next获得一个EventExecutor(事件执行器),由
* 这个执行器来执行任务
*
* EventExecutor 也是 EventExecutorGroup 的一个子类,因此获得了这些方法,对应最终的实现类有NioEventLoop,内部是
* 有一个线程对象的
*/
public abstract class AbstractEventExecutorGroup implements EventExecutorGroup {
/**
*通过next获得一个 EventExecutor 事件执行器,由事件执行器执行该方法
* */
@Override
public <T> Future<T> submit(Runnable task, T result) {
return next().submit(task, result);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return next().submit(task);
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
return next().schedule(command, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return next().scheduleAtFixedRate(command, initialDelay, period, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return next().scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
@Override
public Future<?> shutdownGracefully() {
return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD /* 2 */, DEFAULT_SHUTDOWN_TIMEOUT /* 15 */, TimeUnit.SECONDS);
}
@Override
public <T> List<java.util.concurrent.Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
return next().invokeAll(tasks);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return next().invokeAny(tasks, timeout, unit);
}
@Override
public void execute(Runnable command) {
next().execute(command);
}
}
2.3 MultithreadEventExecutorGroup
- MultithreadEventExecutorGroup 从名字来看是基于多线程的 EventExecutorGroup,内部主要逻辑是完成内部事件执行器的初始化
2.3.1 概览
- MultithreadEventExecutorGroup 内部是多线程,一个线程对应一个 EventExecutor,因此内部很多操作都是基于一个 EventExecutor 数组来操作的,包括初始化以及各种状态判断等
/**
* 基于多线程的 EventExecutor ( 事件执行器 ) 的分组抽象类,用多线程处理任务
*
* Abstract base class for {@link EventExecutorGroup} implementations that handles their tasks
* with multiple threads atthe same time.
*/
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
/**
* EventExecutor 数组,可以理解为一个 EventExecutor 对应一个线程
*/
private final EventExecutor[] children;
/**
* (不可变)只读的EventExecutor数组
*/
private final Set<EventExecutor> readonlyChildren;
/**
* 已终止的EventExecutor数量
*/
private final AtomicInteger terminatedChildren = new AtomicInteger();
/**
* 用于终止EventExecutor的异步 Future
*/
private final Promise<?> terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
/**
* EventExecutor选择器,可以定义
*/
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
/**
* 获取一个事件执行器,用选择器来选一个,默认是轮询
*/
@Override
public EventExecutor next() {
return chooser.next();
}
@Override
public Iterator<EventExecutor> iterator() {
return readonlyChildren.iterator();
}
/**
* Return the number of {@link EventExecutor} this implementation uses. This number is the maps
* 1:1 to the threads it use.
*/
public final int executorCount() {
return children.length;
}
/**
* 创建一个 EventExecutor ,后续next方法返回的就是EventExecutor,这个方法会被服务于 MultithreadEventExecutorGroup的线程调用
* Create a new EventExecutor which will later then accessible via the {@link #next()} method. This method will be
* called for each thread that will serve this {@link MultithreadEventExecutorGroup}.
*/
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
for (EventExecutor l : children) {
l.shutdownGracefully(quietPeriod, timeout, unit);
}
return terminationFuture();
}
@Override
public Future<?> terminationFuture() {
return terminationFuture;
}
@Override
public boolean isShutdown() {
for (EventExecutor l : children) {
if (!l.isShutdown()) {
return false;
}
}
return true;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
long deadline = System.nanoTime() + unit.toNanos(timeout);
loop:
for (EventExecutor l : children) {
for (; ; ) {
long timeLeft = deadline - System.nanoTime();
if (timeLeft <= 0) {
break loop;
}
if (l.awaitTermination(timeLeft, TimeUnit.NANOSECONDS)) {
break;
}
}
}
return isTerminated();
}
}
2.3.2 构造方法
- 构造方法主要是对内部的 EventExecutor 数组做初始化,以及初始化过程的异常处理
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
//1.线程数不能小于0
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
//2.创建执行器,默认是每个任务一个线程
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//3.创建children EventExecutor 数组
children = new EventExecutor[nThreads];
//4.循环创建 EventExecutor 数组中的EventExecutor
for (int i = 0; i < nThreads; i++) {
//是否创建成功
boolean success = false;
try {
//5.创建EventExecutor对象,newChild是抽象方法交给子类实现,
//不同的子类创建的执行器是不同类型的,有 NioEventLoop、KQueueEventLoop等
children[i] = newChild(executor, args);
//6.标记创建成功
success = true;
} catch (Exception e) {
//7.创建失败抛出IllegalStateException异常
//TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
//8.创建失败,关闭所有已创建的 EventExecutor;注意这个成功的标志位 success 是每次循环进来都会赋值,
//因此相当于每一次都会去判断是否失败,只要有一个失败,就会把前面的创建好的EventExecutor关闭
if (!success) {
//9.关闭所有已创建的 EventExecutor
for (int j = 0; j < i; j++) {
children[j].shutdownGracefully();
}
//10.确保所有已创建的 EventExecutor 已关闭,异常的话就等待直到关闭掉
for (int j = 0; j < i; j++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
//11.创建 EventExecutor 选择器,默认选择策略是轮询
chooser = chooserFactory.newChooser(children);
//12.创建监听器,用于 EventExecutor 终止时的监听
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
//已经终止的EventExecutor数量等于总数
if (terminatedChildren.incrementAndGet() == children.length) {
// 设置结果,并通知监听器们。
terminationFuture.setSuccess(null);
}
}
};
//13.给每个EventExecutor设置监听器
for (EventExecutor e : children) {
e.terminationFuture().addListener(terminationListener);
}
//14.创建不可变(只读)的EventExecutor数组,通过工具类返回一个只读的Set,里面包含的 EventExecutor和EventExecutor 数组是一样的
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
2.3.3 ThreadPerTaskExecutor
- ThreadPerTaskExecutor 是默认的事件执行器,每一个任务创建一个新的线程去执行,它直接继承了 JDK的 Executor接口
/**
* 执行器实现类,每个任务创建一个线程执行
*/
public final class ThreadPerTaskExecutor implements Executor {
/**
* 线程工厂
*/
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
/**
* 执行任务
* @param command 任务
*/
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
}
- 关于 ThreadFactory ,有兴趣可以看看Netty中的DefaultThreadFactory,代码比较简单,就是简单的实现了一个命名规则的线程工厂,内部创建的线程都是FastThreadLocalThread对象,FastThreadLocalThread是Thread的子类,是Netty扩展的线程对象,能够快速的访问 FastThreadLocal 变量,这个有时间后续小结一下,这里简单带一下;
三、EventLoopGroup
- EventLoopGroup 接口包含了注册方法和next方法,注册方法将Channel 注册到一个EventLoo,next 方法用于在 EventLoopGroup 中获取一个 EventLoop;
- 接口代码
/**
* EventLoop分组接口,提供了EventLoop的获取方法(next)和将Channel注册到EventLoop的方法
*/
public interface EventLoopGroup extends EventExecutorGroup {
/**
* 获取一个EventLoop来使用
*/
@Override
EventLoop next();
/**
* 注册一个Channel到EventLoop上,注册成功后ChannelFuture会收到一个通知
*/
ChannelFuture register(Channel channel);
ChannelFuture register(ChannelPromise promise);
}
3.1 EventLoopGroup功能
3.1.1 注册Channel
-
前面已经提到,EventLoopGroup 的一个重要功能就是将 Channel 注册到 EventLoop,注册动作实际上是由内部的EventLoop完成的。EventLoopGroup会通过 next 方法选择一个内部的 EventLoop 来执行注册动作(后面会介绍选择策略),最底层本质是在EventLoop的实现子类的内部维护一个集合,集合内部保存注册在自己身上的 Channel 对象。这里可以简单跟一下源码,可能有点绕。
-
首先我们看到 EventLoopGroup的注册功能是在子类 MultithreadEventLoopGroup 实现的,代码如下:
//MultithreadEventLoopGroup 内部选择一个EventLoop来注册
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
- 在 register 方法打上断点启动后调用栈如下:
- 大致的调用流程是:
AbstractBootstrap#bind() ->
-> AbstractBootstrap#doBind()
-> AbstractBootstrap#initAndRegister()
-> MultithreadEventLoopGroup#register
-> EventLoopGroup#register(io.netty.channel.Channel)
-> SingleThreadEventLoop#register(io.netty.channel.ChannelPromise)
- 1.首先是启动器的 bind 方法,进入 bind 逻辑,调用 AbstractBootstrap 的 bind 和 doBind 方法
- 2.然后进入 initAndRegister 方法完成注册逻辑,内部会通过 config 配置对象获取到 EventLoopGroup 对象,调用 EventLoopGroup 的 register 方法
- 3.往后继续走 MultithreadEventLoopGroup 的register 方法,通过next 获取一个内部的 EventLoop 对象,执行 register,然后就走到了 EventLoop 的register 方法
- 4.走到 EventLoop 的实现类 SingleThreadEventLoop 的 register 方法
- 5.继而走到与 SingleThreadEventLoop 所关联的 AbstractChannel 的 register 方法
- 6.最后注册逻辑走到 AbstractNioChannel 的register 方法,(核心代码如下):我们看看到注册的代码是不是和NIO几乎一样,形如:channel.register(selector) 返回selectKey,有兴趣可以查阅参考文章[3]的2.2 示例代码
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (; ; ) {
try {
//这段代码,是不是和NIO代码很相似
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
// TODO TODO 1003 doRegister 异常
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
3.1.2 执行Runnable任务
-
EventLoopGroup 继承了 EventExecutorGroup,前面分析了EventExecutorGroup 继承了线程池的许多接口,通过 EventLoopGroup 的submit 或者 schedule 等方法能够向 EventLoopGroup 提交任务,对应的任务会被 EventLoopGroup 选择一个内部的 EventLoop 来执行,而选择的策略和前面一样,通过 next() 方法实现, 异步任务返回一个 Future 对象,
-
这里需要额外提一下的是 EventExecutorGroup 继承了JDK的 ScheduledExecutorService 方法,由此实现了 submit,schedule 等执行任务的方法,但是返回的对象改变了,父类返回的是JDK的Future对象,EventExecutorGroup 返回的是 Netty自己的Future对象。
-
这里大致的回顾最前面的继承图可以看出来,纵向大致分为两块,右边红圈部分是Group相关的,包括 EventExecutorGroup 和 EventLoopGroup 等,左边没有圈起来的基本上就是 执行器相关,就是被Group 管理的,比如 EventExecutor和 EventLoop。然后发现左边的继承了右边的接口,比如 EventExecutor 继承了 EventExecutor, EventLoop 继承了 EventLoopGroup,因为Group 所提供的功能,本质上内部都是调用内部持有的具体执行器来做,比如submit,register 等,因此左边的也实现了右边的接口,右边的只是管理者,接口通过netx 其实还是委托给内部的对象来执行;
3.2 关于next
- 这里屡一下next这个方法,这个方法对于 EventLoopGroup 来说是获取一个内部的 EventLoop,因为前者是一组后者,而在顶层接口 EventExecutorGroup 中也有一个next 方法用于获取 EventExecutor,因为前者也是是一组后者。这两组对象是不是很相似,我们看看继承图,发现其实:EventLoopGroup 就是对应着 EventExecutorGroup,而 EventLoop 也对应着 EventExecutor
- 从上图看到,二者就是对应的关系,而且 EventExecutorGroup的next 方法在 MultithreadEventExecutorGroup 中得到实现,而 EventLoopGroup 的实现类 MultithreadEventLoopGroup 直接从 MultithreadEventLoopGroup继承到实现的方法,将获取到的 EventExecutor直接向下转型成 EventLoop
//MultithreadEventExecutorGroup中对 EventExecutorGroup 的 next 方法的实现
/**
* 获取一个事件执行器,用选择器来选一个,默认是轮询
*/
@Override
public EventExecutor next() {
return chooser.next();
}
/**
*MultithreadEventLoopGroup中对 EventLoopGroup 的 next 方法的实现,直接调用父类也就
*是 MultithreadEventExecutorGroup的next方法,将获取到的 EventExecutor转换为 EventLoop
*/
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
3.3 EventExecutorChooser
-
这里的 next()方法使用一个选择策略去选择,对应的策略接口是:EventExecutorChooser 它是:EventExecutorChooserFactory的一个内部类,而后者是创建策略的工厂对象,在NioEventLoop 的构造方法可以将 EventExecutorChooserFactory 作为参数传进去来自定义选择策略。
-
EventExecutorChooserFactory 和 EventExecutorChooser 接口
/**
* EventExecutorChooser 工厂接口,可以创建:EventExecutorChooser 事件执行选择器
* Factory that creates new {@link EventExecutorChooser}s.
*/
@UnstableApi
public interface EventExecutorChooserFactory {
/**
* 工厂接口用于创建一个 EventExecutorChooser 选择器对象
* Returns a new {@link EventExecutorChooser}.
*/
EventExecutorChooser newChooser(EventExecutor[] executors);
/**
* EventExecutorChooser 选择器接口
* Chooses the next {@link EventExecutor} to use.
*/
@UnstableApi
interface EventExecutorChooser {
/**
* 选择器用于获取一个事件执行器
* Returns the new {@link EventExecutor} to use.
*/
EventExecutor next();
}
}
- EventExecutorChooser 的实现有两种,都是采用轮询的方式获取,不过计算方式有区别,一种是取余,一种是与运算(长度为2的幂次时)
/**
* 默认 EventExecutorChooser 工厂实现类,简单轮询选择 EventExecutor
* <p>
* Default implementation which uses simple round-robin to choose next {@link EventExecutor}.
*/
@UnstableApi
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
/**
* 单例
*/
public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();
private DefaultEventExecutorChooserFactory() {
}
@SuppressWarnings("unchecked")
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
//长度为2的幂次,就使用与运算
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;
}
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
/**
* 自增序列
*/
private final AtomicInteger idx = new AtomicInteger();
/**
* EventExecutor 数组
*/
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
/**
* 自增与运算,相当于轮询,其实和则增取余差不多,只不过当 executors 长度为2的幂次时,取余运算和
* 这里的与运算等价,不过与云运算性能更好,类似于 hashMap 里面的,非常细微的优化
* 在前面的 newChooser 方法选择的时候会判断如果长度是2的幂次就选这个,不是就选择轮询
*/
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
/**
* 自增序列
*/
private final AtomicInteger idx = new AtomicInteger();
/**
* EventExecutor 数组
*/
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
/**
* 自增取余,相当于轮询
*/
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
}
3.4 MultithreadEventLoopGroup
- MultithreadEventLoopGroup:它一方面继承了MultithreadEventExecutorGroup,是一个多线程的事件执行器,另一方面实现了 EventLoopGroup,因此是一个多线程版本的:EventLoopGroup
- MultithreadEventLoopGroup 基本是就是坐吃现成,所有的方法都在父类 MultithreadEventExecutorGroup 实现好了,继承自 EventLoopGroup 的方法只需要通过父类的 next 选择一个 EventLoop 执行就搞定了,代码不多;
/**
* 多线程版本的 EventLoopGroup 分组抽象类,实现 EventLoopGroup接口并继承MultithreadEventExecutorGroup,使用多线程同时处理任务
* <p>
* Abstract base class for {@link EventLoopGroup} implementations that handles their tasks with multiple threads at
* the same time.
* <p>
* 从代码分析来看,MultithreadEventLoopGroup 本身没有做太多事情,因为多线程相关的初始化工作以及next 选择执行器的方法都
* 已经在父类 MultithreadEventExecutorGroup 中实现好了,继承自 EventLoopGroup 的需要执行的方法则是通过 next 方法获
* 取具体的 EventLoop 对象来执行
* <p>
*/
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(MultithreadEventLoopGroup.class);
/**
* 默认EventLoop线程数,通过static代码块赋值,CPU核心数的2倍
* 为什么是2,因为线程超线程技术一个核心可以对应两个线程
*/
private static final int DEFAULT_EVENT_LOOP_THREADS;
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);
}
}
//下面是各种重载的构造方法
/**
* @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)
*/
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
/**
* @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, ThreadFactory, Object...)
*/
protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
}
/**
* @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor,
* EventExecutorChooserFactory, Object...)
*/
protected MultithreadEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, chooserFactory, args);
}
/**
* 默认是Netty 自定义的命名线程池
*/
@Override
protected ThreadFactory newDefaultThreadFactory() {
return new DefaultThreadFactory(getClass(), Thread.MAX_PRIORITY);
}
/**
* 通过父类 MultithreadEventExecutorGroup 已经实现好的 next 方法获取EventExecutor 事
* 件执行器然后转型为 EventLoop,父类的next 方法是对 EventExecutorGroup 中 next 方法的实现
*/
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
/**
* 下面是继承自EventLoopGroup 和 MultithreadEventExecutorGroup 的方法,基本也是通过next来实
* 现的,或者不实现
*/
@Override
protected abstract EventLoop newChild(Executor executor, Object... args) throws Exception;
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return next().register(promise);
}
@Deprecated
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
return next().register(channel, promise);
}
}
- 需要注意的是默认线程数是 CPU核心 * 2,因为超线程技术一个核心可以处理两个线程 (不过超线程技术达到的性能并不等于两个核心,还需要硬件以及操作系统的相关执行)
3.5 NioEventLoopGroup
-
NioEventLoopGroup 是本文涉及到的唯一一个实现类,前面都是上层接口和抽象类,NioEventLoopGroup 是我们代码中具体创建的对象,因为上层的各种实现,到这代码已经不多了。
-
在分析之前我们看看 NioEventLoopGroup 和其父类 MultithreadEventLoopGroup 的关系以及其他的兄弟实现类,MultithreadEventLoopGroup 代表多线程的实践循环组,其他他有很多实现,(如下), 换言之这些实现类都是基于多线程实现了EventLoopGroup,但是具体适用场景有所不同:
MultithreadEventLoopGroup子实现类 | 功能 |
---|---|
NioEventLoopGroup | 使用Selector和Channel的NIO实现 |
DefaultEventLoopGroup | 默认实现,用于本地通信 |
EpollEventLoopGroup | 只适用于Linux系统,底层使用 epoll |
KQueueEventLoopGroup | 没有注释,暂不清楚 |
- 源码
/**
* NioEventLoop 分组的实现类,多线程,基于 Channel和Selector 的NIO实现
*
* NioEventLoop 本身代码不多,主要方法是:
* 1.构造方法,支持各种参数的构造方法
* 2.IO时间比例设置,调用内部的 NioEventLoop 来设置
* 3.重建 selector,解决 epoll bug
* 4.创建执行器,创建的是 NioEventLoop 实例,创建过程会用到SelectorProvider
* <p>
* {@link MultithreadEventLoopGroup} implementations which is used for NIO {@link Selector} based {@link Channel}s.
*/
public class NioEventLoopGroup extends MultithreadEventLoopGroup {
/**
* 构造方法,很多重载的,部分省略
* Create a new instance using the default number of threads, the default {@link ThreadFactory} and
* the {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
*/
public NioEventLoopGroup() {
this(0);
}
/**
* 这个构造方法参数最多
*
* */
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory,
final RejectedExecutionHandler rejectedExecutionHandler) {
super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory, rejectedExecutionHandler);
}
/**
* 设置事件循环中期望的IO比例,默认50,此时IO和非IO时间各占一半左右
* <p>
* Sets the percentage of the desired amount of time spent for I/O in the child event loops. The default value is
* {@code 50}, which means the event loop will try to spend the same amount of time for I/O as for non-I/O tasks.
*/
public void setIoRatio(int ioRatio) {
for (EventExecutor e : this) {
((NioEventLoop) e).setIoRatio(ioRatio);
}
}
/**
* 用新创建的 Selector 替换子事件循环中的 Selector,解决 epoll 的bug。(导致CPU 100%)
* Replaces the current {@link Selector}s of the child event loops with newly created {@link Selector}s to work
* around the infamous epoll 100% CPU bug.
*/
public void rebuildSelectors() {
for (EventExecutor e : this) {
((NioEventLoop) e).rebuildSelector();
}
}
/**
* 创建 EventLoop,内部构造的是 NioEventLoop
* 父类 MultithreadEventExecutorGroup 初始多线程的时候调用该类实现
*/
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(
this,
executor,
(SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(),
(RejectedExecutionHandler) args[2]);
}
}
- 这里主要是 newChild 这个方法,这个方法会被父类 MultithreadEventExecutorGroup 调用,用于初始化多线程的执行器,父类调用会将参数传过来,而父类的参数则是在构造 NioEventLoopGroup 的时候通过super 传给父类(SelectorProvider 参数很关键);
1.构造方法,支持各种参数的构造方法
2.IO时间比例设置,调用内部的 NioEventLoop 来设置
3.重建 selector,解决 epoll bug
4.创建执行器,创建的是 NioEventLoop 实例,创建过程会用到SelectorProvider