目录
Summary
NioEventLoopGroup是MultithreadEventLoopGroup这个抽象类的具体实现、其专门是为注册到Selector上的Channel服务、当某个Channel上的事件ready(包括但不局限于、read/write/listen)后、具体的线程就是由NioEventLoopGroup提供用以处理以上ready的各种事件。
NioEventLoopGroup实际上是一个连接池、NioEventLoopGroup在后台启动了N(默认是cpu颗数 * 2)个NioEventLoop来处理Channel事件、每个NioEventLoop负责M个Channel。
First
首先该类实现了父类定义的newChild(Executor executor, Object args); NioEventLoopGroup这个连接池内的每一个线程都是一个NioEventLoop
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
// 实现父类定义的方法、并且返回NioEventLoop
return new NioEventLoop(this, executor, (SelectorProvider) args[0]);
}
该类中除了以上定义的方法外、还定义了避免JDK空轮询的方案实现、即rebuildSelectors()
/**
* 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: children()) {
((NioEventLoop) e).rebuildSelector();
}
}
该类的继承结构如下
我们从继承关系的拓扑中、从下往上逐个类进行解读
Second
MultithreadEventLoopGroup、该类是上文NioEventLoopGroup的父级抽象类、其是EventLoopGroup的大部分方法功能的实现子类、doc明确说明该类可以在同一个时间点可以提供多个线程并发的读写数据流。
该类的构造方法皆都是直接调用父类的构造(即该类的所有方法、除了抽象方法以外、其他方法都是调用父类的实现方法的)。除此之外、该类还明确了初始化的线程个数、如果没有显示的告诉NioEventLoopGroup初始化几个线程的话、代码如下
// 默认线程池内的线程个数
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
/**
* 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}.
*
* 这个方法之所以是@Override的是因为该类从MultithreadEventExecutorGroup中继承而来、但是
* MultithreadEventLoopGroup却没有实现、而是教给了NioEventLoopGroup来实现
*/
@Override
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
另外、该类还负责了Channel的注册操作、当应用代码调用到bind()方法时候、会将Channel注册到Selector上、这个操作也是由NioEventLoopGroup来负责的。
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
Thrid
MultithreadEventExecutorGroup该类的构造方法中、给线程池(ForkJoinPool)初始化每个线程(NioEventLoop)、并且给每个NipEventLoop都添加监听事件。
并且、在该类真类中定义了抽象方法newChild(Executor executor, Object ... args)方法
在这里我想赘述下、Netty中处于性能考虑和处理速度的考虑、其很多地方都是使用了CAS的方式替代了传统的锁、例如:在当前类MultithreadEventExecutorGroup中的awaitTermination就是一个比较典型的使用场景、代码如下
// 这是给一个线程设置超时关闭的方法、
@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;
}
// 如果给线程设置成功、则break当前循环、继续下一个线程的超时中断设置工作
if (l.awaitTermination(timeLeft, TimeUnit.NANOSECONDS)) {
break;
}
}
}
return isTerminated();
}
除此之外、当前类中还有个设计模式内策略模式的应用案例
// 定义抽象接口、内含有一个抽象方法next()
private interface EventExecutorChooser {
EventExecutor next();
}
// 子类一 如果线城数是2的幂次数的话、这种索引计算的选择是要高于子类二的!
private final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
@Override
public EventExecutor next() {
return children[childIndex.getAndIncrement() & children.length - 1];
}
}
// 子类二 普通的数组索引选择
private final class GenericEventExecutorChooser implements EventExecutorChooser {
@Override
public EventExecutor next() {
return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}
}
Fourth
AbstractEventExecutorGroup该类是EventExecutorGroup的抽象实现类、均通过next()方法拿到一个线程以后执行所以方法。
注意该类中execute()方法在子类中的实现、将是一个重中之重、后篇会着重介绍!
Fifty
Netty顶层接口。EventExecutorGroup负责通过定义的next()方法提供EventExecutor即线程、并且负责所有EventExecutor的生命周期、并能全局的关闭他们