阅读须知
- Netty版本:4.1.14.Final
- 文章中使用/* */注释的方法会做深入分析
正文
在分析BootStrap源码时我们给出的示例程序中,我们看到了NioEventLoopGroup的身影,之前我们分析了NioEventLoop的源码,从命名上就可以看出,NioEventLoopGroup维护了一组NioEventLoop,我们首先看一下它的构造方法,默认无参构造方法的调用链如下:
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup( int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object...args) {
/* nThreads为0也就是默认情况下,默认的线程数量为CPU核数*2 */
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object...args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object...args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 初始化NioEventLoop数组,NioEventLoop实现了EventExecutor
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i++) {
boolean success = false;
try {
// 子类实现,NioEventLoopGroup的实现即为创建NioEventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
for (int j = 0; j < i; j++) {
// 优雅停止NioEventLoop
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
// 如果NioEventLoop没有终止,需要等待其终止
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// 调用者处理中断
Thread.currentThread().interrupt();
break;
}
}
}
}
}
// 初始化NioEventLoop选择器
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object> () {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
// 为每一个NioEventLoop添加终止监听器
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor> (children.length);
Collections.addAll(childrenSet, children);
// 构建只读不可修改的NioEventLoop数组
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
这样,NioEventLoopGroup的构造就完成了,前文我们提到,NioEventLoopGroup维护了一组NioEventLoop,所以NioEventLoopGroup对事件和任务的执行也就是依赖维护的NioEventLoop来完成的,NioEventLoopGroup对NioEventLoop的选择是调用next方法来完成的:
MultithreadEventLoopGroup:
public EventLoop next() {
return (EventLoop) super.next();
}
MultithreadEventExecutorGroup:
public EventExecutor next() {
return chooser.next();
}
这里的chooser是在构造方法中初始化的:
DefaultEventExecutorChooserFactory:
public EventExecutorChooser newChooser(EventExecutor[] executors) {
// 判断NioEventLoop的数量是否是2的次方
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
我们来看两个chooser的next方法实现:
DefaultEventExecutorChooserFactory.PowerOfTwoEventExecutorChooser:
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
这里用一个AtomicInteger类型的idx变量作为自增计数,与NioEventLoop数组的长度进行按位与计算,相当于取模操作,但是比取模效率要高很多。
DefaultEventExecutorChooserFactory.GenericEventExecutorChooser:
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
很简单,这里就是一个简单的取模操作。两者的实现是一样的,自增取模其实也就是轮询选择NioEventLoop,但是前者的效率更高,所以我们在日常应用的过程中也尽量将线程的数量设置为2的次方。
NioEventLoopGroup的源码我们就分析到这。