Netty源码解析之NioEventLoopGroup

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/heroqiang/article/details/81630558

阅读须知

  • 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的源码我们就分析到这。

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/81630558