全网最通俗易懂的netty源码--EventLoop的实例化
前面的group的构造中,创建了一个children数组,然后循环为每个元素调用下面这行代码去实例化NioEventLoop。
children[i] = newChild(executor, args);
这行代码当时并没有详细分析,只是以一个概括的方式描述了它的作用。因为逻辑也是有点复杂的,所以另开一篇文章进行分析。newChild(…)方法具体如下:
NioEventLoopGroup
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
它调用了 NioEventLoop 的构造方法:
NioEventLoop
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider
selectorProvider,SelectStrategy strategy,
RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
//开启 NIO 中最重要的组件:Selector
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
NioEventLoopGroup 代表线程池,NioEventLoop 就是其中的线程。
NioEventLoopGroup 是线程 NioEventLoop 的 parent,从上面代码的取名可以看出。
每个 NioEventLoop 都有 Selector,上面代码也反应了这一点,这和 Tomcat 的 NIO 模型有点区别。
executor、selectStrategy和rejectedExecutionHandler从NioEventLoopGroup中一路传到了 NioEventLoop 中。
结合构造函数看看NioEventLoop 类的属性:
provider:由 NioEventLoopGroup传进来,一个线程池有一个 selectorProvider,用于创建 Selector 实例
selector:线程池中每个线程都有一个 Selector 实例。
selectStrategy:select 操作的策略,这个不急。
ioRatio:IO 任务的执行时间比例,每个线程既有 IO 任务,也有非 IO 任务,该参数为了保证有足够时间是给 IO 的。
上面的构造方法点击super,进入如下代码:
SingleThreadEventLoop
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
//直接忽略
tailTasks = newTaskQueue(maxPendingTasks);
}
继续调用父类的构造函数
SingleThreadEventExecutor
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
//设置了parent,也就是之前创建的NioEventLoopGroup实例
//NioEventLoop有个属性叫parent,这里就是将该属性和NioEventLoopGroup实例关联起来
super(parent);
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);
//NioEventLoopGroup构造时实例化了一个ThreadPerTaskExecutor,用来开启 NioEventLoop中的
//(Thread实例)
this.executor = ObjectUtil.checkNotNull(executor, "executor");
//taskQueue,任务队列很重要,提交给NioEventLoop的任务都会进入到taskQueue中等待
//这个queue的默认容量是16
taskQueue = newTaskQueue(this.maxPendingTasks);
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler,
"rejectedHandler");
}
NioEventLoop的父类是SingleThreadEventLoop,而SingleThreadEventLoop的父类是 SingleThreadEventExecutor,从名字可以看出,而且是单线程的线程池(Executor,线程池)。这就有意思了,线程池NioEventLoopGroup的每个线程NioEventLoop也是一个线程池来。(之所以用线程池:主要是为了用线程池的队列。单线程也避免了多线程的切换带了的性能损失和加锁同步等麻烦)
taskQueue:任务队列,NioEventLoop需要处理IO和非IO事件,通常它都在执行selector的select方法或者正在处理selectedKeys,如果我们要给它submit一个任务,任务就会被放到taskQueue中,等待轮询。
rejectedExecutionHandler:taskQueue 的默认容量是 16,所以,如果 submit 的任务堆积了到了 16,再往里面提交任务会触发 rejectedExecutionHandler 的执行策略。默认的抛出RejectedExecutionException 异常
private static final RejectedExecutionHandler REJECT = new RejectedExecutionHandler() {
@Override
public void rejected(Runnable task, SingleThreadEventExecutor executor) {
throw new RejectedExecutionException();
}
};
再回到NioEventLoop的构造方法:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider
selectorProvider,SelectStrategy strategy,
RejectedExecutionHandler rejectedExecutionHandler) {
//我们刚刚说完了这个
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
//开启NIO中最重要的组件:Selector
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
这一行代码我们在上面花了大篇幅讲解
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
剩下的内容就比较简单了,主要看这两行
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
这两行代码就是在创建Selector,很简单。
总结
1、将NioEventLoop实例的parent,和对应的NioEventLoopGroup实例关联起来,其实就是赋值。
2、将NioEventLoopGroup构造时定义的executor传入进来,赋值给NioEventLoop实例
3、创建一个taskQueue,默认容量为16
4、创建Selector组件
5、上面这几步做好之后,一个NioEventLoop实例也就创建好了,返回并赋值给group的children数组。