在之前的ServerBootstrap示例中,有两句代码比较独特
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(DEFAULT_EVENT_LOOP_THREADS);
通过其实现接口EventLoopGroup的注解我们了解到,
/**
* Special {@link EventExecutorGroup} which allows registering {@link Channel}s that get
* processed for later selection during the event loop.
*
*/
public interface EventLoopGroup extends EventExecutorGroup {}
EventLoopGroup主要提供给channel,供其注册使用。(之前我们都是将channel注册到selector上的),那么说明EventLoopGroup应该提供了注册方法和selector的相关实现。
later selection during the event loop,根据这句话,我们能够理解到真正让我们注册channel的应该是EventLoopGroup中的一个叫做EventLoop的东东。
通过注释,我们能够大概明白EventLoopGroup与EventLoop的关系,就是EventLoopGroup是一组EventLoop的集合,同时EventLoopGroup提供了选择方法来选择一个EventLoop供channel注册使用。
究竟是不是这样呢?我们继续看。
1.EventLoopGroup类结构图分析我们从上往下看。
Executor、ExecutorService接口属于JDK本身就有的接口,我们之前比较常用的其实现类就是 ThreadPoolExecutor,用于线程池的实现;
Iterable接口就不用说了,遍历的时候常用;
ScheduleExecutorService接口,类似于ExecutorService,只不过是一种提供定时任务执行的线程池;
以上都是JDK自带的接口。
真正Netty的接口是EventExecutorGroup和EventLoopGroup
能肉眼看到的与其他接口不同的方法就是以下两个:
// 获取下一个EventLoop
EventLoop next()
// 注册channel
ChannelFuture register(Channel channel)
就是在前言中提到的,作为EventLoop的集合,EventLoopGroup会提供一个选择方法用于获取一个EventLoop,目前看来就是next方法了;
同时提供一个针对channel的注册方法,可以将channel注册到对应的EventLoop上(理论上EventLoop应该有一个Selector的实现类)
总结:通过对其类结构图的分析,EventLoopGroup主要提供了以下三点功能:
1)类似于线程池的任务执行
2)根据一定的规则获取一个EventLoop
3)提供register方法,让Channel可以注册到EventLoopGroup上
2.EventLoop类结构图分析蛮有意思的一点是,EventLoop继承了EventLoopGroup接口,也就是上述我们所提到的EventLoopGroup的方法,在EventLoop中都有所实现;
EventLoop自己带有的接口也仅仅是EventExecutor和OrderedEventEexcutor,如下所示
OrderedEventEexcutor只是一个标记接口;
EventExecutor中的方法基本都是自有方法;
所以那EventLoop基本与EventLoopGroup没啥区别了,都有next()方法,都可以执行提交任务,EventLoop相较EventLoopGroup多的方法就是EventExecutor中提供的方法了。
下面我们通过源码来比较下EventLoop与EventLoopGroup的关键性区别
我们来关注下NioEventLoopGroup和NioEventLoop这两个实现(当然,也有别的实现,我们在这里只关注NIO的实现)
3.NioEventLoop重点方法释义public final class NioEventLoop extends SingleThreadEventLoop {
private Selector selector;
...
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory queueFactory) {
super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
rejectedExecutionHandler);
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
// 注册方法
public void register(final SelectableChannel ch, final int interestOps, final NioTask task) {
ObjectUtil.checkNotNull(ch, "ch");
if (interestOps == 0) {
throw new IllegalArgumentException("interestOps must be non-zero.");
}
if ((interestOps & ~ch.validOps()) != 0) {
throw new IllegalArgumentException(
"invalid interestOps: " + interestOps + "(validOps: " + ch.validOps() + ')');
}
ObjectUtil.checkNotNull(task, "task");
if (isShutdown()) {
throw new IllegalStateException("event loop shut down");
}
if (inEventLoop()) {
register0(ch, interestOps, task);
} else {
...
}
}
// register0
private void register0(SelectableChannel ch, int interestOps, NioTask task) {
try {
ch.register(unwrappedSelector, interestOps, task);
} catch (Exception e) {
throw new EventLoopException("failed to register a channel", e);
}
}
}
// next方法在其父类AbstractEventExecutor中实现
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor {
...
@Override
public EventExecutor next() {
return this;
}
}
从源码上看:
NioEventLoop.next()方法返回自身
NioEventLoop.register()方法,提供了一个注册方法注册到Selector上(从其构造方法上看,每一个NioEventLoop都有一个Selector的实现)
4.NioEventLoopGroup重点方法释义// NioEventLoopGroup构造方法
public class NioEventLoopGroup extends MultithreadEventLoopGroup {
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
// 这里的super最终到达MultithreadEventExecutorGroup中
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
}
// MultithreadEventExecutorGroup构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
...
// 这里的children集合就是EventLoop的集合
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 创建EventLoop对象,保存到数组中
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
...
}
}
// next方法中的选择器
chooser = chooserFactory.newChooser(children);
...
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
...
}
// next方法的实现在其父类MultithreadEventExecutorGroup中实现
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
@Override
public EventExecutor next() {
return chooser.next();
}
}
// 注册方法在其父类MultithreadEventLoopGroup
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
...
@Override
public ChannelFuture register(Channel channel) {
// 通过next方法选择一个EventLoop之后,在调用EventLoop.register方法,将channel注册上
return next().register(channel);
}
...
}
// 选择器类工厂DefaultEventExecutorChooserFactory
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
// 默认提供了两种选择器
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
}
总结:
NioEventLoopGroup的构造方法,用于创建指定数量的EventLoop,存放到数组中;
根据EventLoop的数量,并创建一个选择器;
next方法,通过创建的选择器Chooser,来选择一个EventLoop使用;
register方法,通过选择出来的EventLoop,调用其注册方法将channel注册其上(也就是将Channel注册到EventLoop的selector上);
总结:以上就是笔者在学习Netty中关于EventLoop和EventLoopGroup时的心得。
Netty框架实在太大了,代码量居多。初学者很容易陷入无尽的代码细节中,然后就放弃了。
针对框架而言,应该先观察其大概结构,不要陷入细节(尤其初学时),当对它的结构有一个大致了解时,再去深入其细节。
所以,笔者在本文中,也只是介绍了其最重要的几个方法,比如如何将Channel注册到EventLoop上,如何从EventLoopGroup中选择一个合适的EventLoop,这才是我们的学习重点。
对于其他方法,比如线程池的方法执行、Chooser的实现等,建议读者自行阅读,笔者不再详述。
后续,我们对EventLoop的一些关键运行方法再继续介绍。