EventLoopGroup到底是个啥?

Github地址:github.com/lhj502819/n…,示例代码在example模块中

在Netty中,Channel为网络操作抽象类,EventLoop负责处理注册到其上的Channel处理I/O操作,两者配合参与I/O操作;
EventLoopGroup是一个EventLoop的分组,它可以获取到一个或者多个EventLoop对象,因此它提供了迭代出EventLoop对象的方法。

在我们的使用示例中,使用到的就是:

  • new NioEventLoopGroup(),创建一个EventLoopGroup对象
  • EventLoopGroup#register(Channel channel),将Channel注册到EventLoopGroup上

在这里插入图片描述
红色部分为EventLoopGroup相关类,今天我们只学习这部分的内容

EventExecutorGroup

EventExecutorGroup负责提供EventExecutor以通过其next()方法使用。 除此之外,它还负责处理它们的生命周期并允许以全局方式关闭它们。
继承了ScheduledExecutorServiceIterable<EventExecutor>,表明了是可定时的,并且提供EventExecutor存储及遍历功能。
EventExecutorGroup自身不执行任务,而是将任务submit或者schedule给自己管理的EventExecutor的分组,至于提交给哪一个EventExecutor,一般是通过next()方法选择一个EventExecutor,但是也只是提供了行为,具体的还要看实现类。

AbstractEventExecutorGroup

对接口进行了抽象实现,但并未具体实现,比如{@link #submit(Runnable)} 只是提供了提交任务的方式,但具体实现还是由子类去实现。代码就不贴了,感兴趣大家自己看下就行哈,很简单的。

MultithreadEventExecutorGroup

通过名称就可以看出来,是EventExecutor的多线程版本。

变量

/**
 * EventExecutor集合
 */
private final EventExecutor[] children;
/**
 * 只读的EventExecutor数组
 */
private final Set<EventExecutor> readonlyChildren;
/**
 * 已终止的Executor数量
 */
private final AtomicInteger terminatedChildren = new AtomicInteger();
/**
 * 用于终止EventExecutor的异步Future
 */
private final Promise<?> terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
/**
 * EventExecutor选择器
 */
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
复制代码

构造方法

/**
 * Create a new instance.
 *
 * @param nThreads          the number of threads that will be used by this instance.
 * @param threadFactory     the ThreadFactory to use, or {@code null} if the default should be used.
 * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
 */
protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
    this(nThreads, threadFactory == null ? null : new ThreadPerTaskExecutor(threadFactory), args);
}

/**
 * Create a new instance.
 *
 * @param nThreads          the number of threads that will be used by this instance.
 * @param executor          the Executor to use, or {@code null} if the default should be used.
 * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

/**
 * Create a new instance.
 *
 * @param nThreads          the number of threads that will be used by this instance.
 * @param executor          the Executor to use, or {@code null} if the default should be used.
 * @param chooserFactory    the {@link EventExecutorChooserFactory} to use.
 * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    checkPositive(nThreads, "nThreads");

    /**
     * 如果传入的执行器为空,则使用默认的执行器ThreadPerTaskExecutor
     */
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    //创建EventExecutor数组
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            //创建Executor对象
            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 {
            if (!success) {
                //如果创建失败,则关闭所有已创建的EventExecutor
                for (int j = 0; j < i; j ++) {
                    children[j].shutdownGracefully();
                }
                //确保所有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;
                    }
                }
            }
        }
    }

    //创建EventExecutor选择器
    chooser = chooserFactory.newChooser(children);

    //创建监听器,用于监听EventExecutor终止,
    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
            //回调逻辑,当所有EventExecutor都终止完成时,通过调用Promise#setSuccess方法,通知监听器们
            if (terminatedChildren.incrementAndGet() == children.length) {
                terminationFuture.setSuccess(null);
            }
        }
    };

    for (EventExecutor e: children) {
        //设置监听器到每个EventExecutor上
        e.terminationFuture().addListener(terminationListener);
    }

    //创建不可变的EventExecutor数组
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
复制代码

注意事项(这里我们重点关注最终的构造方法):

  • EventExecutorChooserFactory默认使用的是DefaultEventExecutorChooserFactory,用来在多个Executor中选择一个Executor去执行任务
  • 如果我们没有指定executor,那么会使用ThreadPerTaskExecutor(下边有提到)
  • #49行创建Executor,会为每个线程都创建一个Executor,随后会通过next()方法访问到,具体的创建过程由子类实现

ThreadPerTaskExecutor

每个任务一个线程的的执行器实现类

EventLoopGroup

继承了EventExecutorGroup,提供注册Channel能力的EventExecutorGroup。

MultithreadEventLoopGroup

继承了MultithreadEventExecutorGroup,又实现了EventLoopGroup,基于MultithreadEventExecutorGroupChannel的注册进行行为抽象。

static {
        /**
         * 当未指定线程数时,默认的线程数为 CPU核心数 * 2 ,因为目前的CPU都是超线程,一个CPU可对应2个线程
         */
        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);
        }
}

@Override
public EventLoop next() {
    return (EventLoop) super.next();
}


@Override
public ChannelFuture register(Channel channel) {
    //获取一个EventLoop,将Channel注册上去
    return next().register(channel);
}
复制代码

NioEventLoopGroup

基于Java NIO Selector的Executor实现,NioEventLoop实例就是在这里创建的,Selector也是在这里和EventLoop绑定的,同时也对JDK使用epoll的bug作了处理。

/** 
 * 我们重点关注下其构造方法的各个参数即可,这里也看到了我们心心念念的Selector
 * @param nThreads the number of threads that will be used by this instance.
 * @param executor the Executor to use, or {@code null} if default one should be used.
 * @param chooserFactory the {@link EventExecutorChooserFactory} to use.
 * @param selectorProvider the {@link SelectorProvider} to use.
 * @param selectStrategyFactory the {@link SelectStrategyFactory} to use.
 * @param rejectedExecutionHandler the {@link RejectedExecutionHandler} to use.
 * @param taskQueueFactory the {@link EventLoopTaskQueueFactory} to use for
 *                         {@link SingleThreadEventLoop#execute(Runnable)},
 *                         or {@code null} if default one should be used.
 * @param tailTaskQueueFactory the {@link EventLoopTaskQueueFactory} to use for
 *                             {@link SingleThreadEventLoop#executeAfterEventLoopIteration(Runnable)},
 *                             or {@code null} if default one should be used.
 */
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
                         SelectorProvider selectorProvider,
                         SelectStrategyFactory selectStrategyFactory,
                         RejectedExecutionHandler rejectedExecutionHandler,
                         EventLoopTaskQueueFactory taskQueueFactory,
                         EventLoopTaskQueueFactory tailTaskQueueFactory) {
    super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory,
            rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}


/**
 * 创建NioEventLoop,用来执行事件,这里的args就是从构造方法中传到父类的,父类中又调用子类实现,前后呼应有没有,
 * 是前后呼应,但我也没感觉到这种设计的优点^_^
 */
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    SelectorProvider selectorProvider = (SelectorProvider) args[0];
    SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
    RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
    EventLoopTaskQueueFactory taskQueueFactory = null;
    EventLoopTaskQueueFactory tailTaskQueueFactory = null;

    int argsLength = args.length;
    if (argsLength > 3) {
        taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
    }
    if (argsLength > 4) {
        tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
    }
    return new NioEventLoop(this, executor, selectorProvider,
            selectStrategyFactory.newSelectStrategy(),
            rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}


/**
 * 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.
 *
 * 用来处理JDK使用epoll的100%CPU bug,当触发bug时,NioEventLoop会自动调用此方法进行重建Selector对象,以修复该问题
 */
public void rebuildSelectors() {
    for (EventExecutor e: this) {
        ((NioEventLoop) e).rebuildSelector();
    }
}

/**
 * Replaces the current {@link Selector} of this event loop with newly created {@link Selector}s to work
 * around the infamous epoll 100% CPU bug.
 * 
 * 看这注释表达了作者对JDK NIO的不满,哈哈,臭名昭著的bug
 */
public void rebuildSelector() {
    if (!inEventLoop()) {
        execute(new Runnable() {
            @Override
            public void run() {
                rebuildSelector0();
            }
        });
        return;
    }
    rebuildSelector0();
}

复制代码

总结

EventLoopGroup主要就是对Executor进行的封装,并且是基于JUC的包进行的实现,作者对各个工作组件都进行了很细化的的拆分,也是为了扩展性高。文章中我没有把所有的源码都COCY出来逐句翻译,我们在阅读源码的过程中不用去把每一行代码都搞懂,我们要去体会框架设计的巧妙,对整个框架的内部逻辑走向把握住即可。

image.png

下一篇文章我们会对EventLoop进行分析,敬请期待.

猜你喜欢

转载自juejin.im/post/7050374649191874591