EventLoop和EventLoopGroup的关系
类图如下,EventLoop接口继承了EventLoopGroup的接口,EventLoop和EventLoopGroup是io.netty.channel包中的类,为了与Channel的事件进行交互,EventExecutorGroup是io.netty.util.concurrent包中的类,他直接继承了JDK的java.util.concurrent包的ScheduledExecutorService接口,对ScheduledExecutorService接口进行增强,用于提供线程执行器。
在EventLoop接口中,只定义了一个方法parent(),用于返回他所属的EventLoopGroup。
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}
EventLoop和EventLoopGroup以及Thread、Channel的关系如下图:
这些关系是:
- 一个EventLoopGroup包含一个或者多个EventLoop;
- 一个EventLoop在它的生命周期内只和一个Thread绑定;
- 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理;
- 一个Channel在它的生命周期内只注册于一个EventLoop;
- 一个EventLoop可能会被分配给一个或多个Channel。
源码分析
初始化
以NioEventLoopGroup为例,下面是他的部分类结构图,可以看出它实现了LoopGroup接口。
我们从new NioEventLoopGroup()
开始。
NioEventLoopGroup类
// 这边没传线程数,就默认0
public NioEventLoopGroup() {
this(0);
}
// 。。。部分略
// 这边会调用父类MultithreadEventLoopGroup的构造函数
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
NioEventLoopGroup的几个构造参数:
- nThreads:线程数
- executor:线程池
- selectorProvider:用来获取Selector
- selectStrategyFactory:select操作的策略
- RejectedExecutionHandler:线程池满了,对新的任务应该用的策略
MultithreadEventLoopGroup类,对线程数的处理。
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
// 判断传过来的线程数量是否等于0,等于0从DEFAULT_EVENT_LOOP_THREADS取值
// 在静态代码块中,从系统属性中io.netty.eventLoopThreads获取,缺省值为CPU核心数*2
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
MultithreadEventExecutorGroup类,创建EventExecutor数组,并监听是否terminated
// 传递EventExecutorChooserFactory,这个是用来从EventExecutor数组选择一个EventExecutor
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));
}
// 设置executor,ThreadPerTaskExecutor中的execute方法如下
// threadFactory.newThread(command).start(),说明每次有任务,就创建一个线程
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 设置子类,children相当于线程池的数组
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) {
// 如果失败了,前面已经实例化的线程都要关闭
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
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;
}
}
}
}
}
// 根据线程的数量创建chooser
// 如果线程数是2的幂次方,则使用PowerOfTwoEventExecutorChooser
// 否则使用GenericEventExecutorChooser
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
// terminated的EventExecutor个数如果等于EventExecutor数组的个数,这个线程池就terminated
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
// 为每个EventExecutor都添加监听
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
// 设置只读的EventExecutor数组
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
newChild方法,在NioEventLoopGroup类中,这个后面的args参数,就是我们之前传过来的,其中Selector对象就是在这个方法里通过SelectorProvider的openSelector()获取的,EventLoop都有自己的Selector对象。
@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]);
}
总结一下,MultithreadEventExecutorGroup有个EventExecutor(NioEventLoop继承了SingleThreadEventLoop,SingleThreadEventLoop实现了EventLoop接口,而EventLoop继承了EventExecutor)类型的children,所以相当于NioEventLoopGroup内部有个EventLoop数组,这个EventLoop数组的大小,就是线程数。这些EventLoop的parent都是同一个Executor。
execute
在Channel注册的时候(这个后面讲),会调用SingleThreadEventExecutor的execute方法。这个方法主要限制线程的数量,taskQueue队列的大小最小为16,如果系统属性io.netty.eventLoop.maxPendingTasks没有设置,就默认Integer.MAX_VALUE
。
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
// 当前调用线程是否是支撑 EventLoop 的线程
boolean inEventLoop = inEventLoop();
// 加入到队列,如果队列满了,则根据策略抛出异常
addTask(task);
// 如果不是当前线程
if (!inEventLoop) {
// 启动一个线程
startThread();
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
startThread方法,先判断是否满足启动的条件,实际的启动在doStartThread方法里。
private void startThread() {
// 还没启动
if (state == ST_NOT_STARTED) {
// 通过cas判断是否正确更改状态,这边可能有多个线程进行启动,但是只要一个启动就好了
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
try {
// 开启线程
doStartThread();
} catch (Throwable cause) {
STATE_UPDATER.set(this, ST_NOT_STARTED);
PlatformDependent.throwException(cause);
}
}
}
}
doStartThread,通过ThreadPerTaskExecutor线程池创建一个线程。
private void doStartThread() {
assert thread == null;
// 这个executor就是ThreadPerTaskExecutor,上面提过,每次有任务就创建一个线程
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
// 更新最后执行时间
updateLastExecutionTime();
try {
// 主要调用run方法,在这里执行队列的任务
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 省略
}
}
});
}
总结一下,一个EventLoop只有一个线程,并维护一个任务队列,通过这个线程来处理任务。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。