文章目录
原博文,点击这里
1.NioEventLoop
设计的两大功能:
1、是作为 IO 线程, 执行与 Channel 相关的 IO 操作, 包括 调用 select 等待就绪的 IO 事件、读写数据与数据的处理等;
2、第二个任务是作为任务队列执行任务, 任务可以分为2类:
2.1、普通task:通过调用NioEventLoop的execute(Runnable task)方法往任务队列里增加任务,Netty有很多系统task,创建他们的主要原因是:当io线程和用户线程同时操作网络资源的时候,为了防止并发操作导致的锁竞争,将用户线程的操作封装成Task放入消息队列中,由i/o线程负责执行,这样就实现了局部无锁化。
2.2、定时任务:执行schedule()方法
参考博文,点击这里
每个NioEventLoop里都有一个selector与TaskQueue,当我们在进行一些耗时的操作的时候,会产生阻塞,这时候我们就可以用到TaskQueue
通过前面的学习,我们对NioEventLoop做过如下几点简单的概述:
① NioEventLoop是一个基于JDK NIO的异步事件循环类,它负责处理一个Channel的所有事件在这个Channel的生命周期期间。
② NioEventLoop的整个生命周期只会依赖于一个单一的线程来完成。一个NioEventLoop可以分配给多个Channel,NioEventLoop通过JDK Selector来实现I/O多路复用,以对多个Channel进行管理。
③ 如果调用Channel操作的线程是EventLoop所关联的线程,那么该操作会被立即执行。否则会将该操作封装成任务放入EventLoop的任务队列中。
④ 所有提交到NioEventLoop的任务都会先放入队列中,然后在线程中以有序(FIFO)/连续的方式执行所有提交的任务。
⑤ NioEventLoop的事件循环主要完成了:a)已经注册到Selector的Channel的监控,并在感兴趣的事件可执行时对其进行处理;b)完成任务队列(taskQueue)中的任务,以及对可执行的定时任务和周期性任务的处理(scheduledTaskQueue中的可执行的任务都会先放入taskQueue中后,再从taskQueue中依次取出执行)。
其中几点已经在启动流程的源码分析中做了详细的介绍。本文主要针对NioEventLoop事件循环的流程对NioEventLoop进行更深一步的学习。
2.重要属性
2.1taskQueue
用于存储任务的队列,是一个MpscUnboundedArrayQueue实例。
private final Queue<Runnable> taskQueue;
2.2tailTasks
是一个MpscUnboundedArrayQueue实例。用于存储当前或下一次事件循环(eventloop)迭代结束后需要执行的任务。
private final Queue<Runnable> tailTasks;
2.3scheduledTaskQueue
定时或周期任务队列,是一个PriorityQueue实例。
Queue<ScheduledFutureTask<?>> scheduledTaskQueue;
2.4 SELECTOR_AUTO_REBUILD_THRESHOLD
private static final int MIN_PREMATURE_SELECTOR_RETURNS = 3;
private static final int SELECTOR_AUTO_REBUILD_THRESHOLD;
int selectorAutoRebuildThreshold = SystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
if (selectorAutoRebuildThreshold < MIN_PREMATURE_SELECTOR_RETURNS) {
selectorAutoRebuildThreshold = 0;
}
SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;
SELECTOR_AUTO_REBUILD_THRESHOLD用于标识Selector空轮询的阈值,当超过这个阈值的话则需要重构Selector。
如果有设置系统属性”io.netty.selectorAutoRebuildThreshold”,并且该属性值大于MIN_PREMATURE_SELECTOR_RETURNS(即,3),那么该属性值就为阈值;如果该属性值小于MIN_PREMATURE_SELECTOR_RETURNS(即,3),那么阈值为0。如果没有设置系统属性”io.netty.selectorAutoRebuildThreshold”,那么阈值为512,即,默认情况下阈值为512。
2.5selectNowSupplier
private final IntSupplier selectNowSupplier = new IntSupplier() {
@Override
public int get() throws Exception {
return selectNow();
}
};
selectNow提供器,在事件循环里用于选择策略(selectStrategy)中。
2.6pendingTasksCallable
private final Callable<Integer> pendingTasksCallable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return NioEventLoop.super.pendingTasks();
}
};
@Override
public int pendingTasks() {
// As we use a MpscQueue we need to ensure pendingTasks() is only executed from within the EventLoop as
// otherwise we may see unexpected behavior (as size() is only allowed to be called by a single consumer).
// See https://github.com/netty/netty/issues/5297
if (inEventLoop()) {
return super.pendingTasks();
} else {
return submit(pendingTasksCallable).syncUninterruptibly().getNow();
}
}
因为pendingTasks()方法的底层就是调用taskQueue.size()方法,而前面我们已经说了taskQueue是一个MpscQueue,所以只能由EventLoop所在的线程来调用这个pendingTasks()方法,如果当前线程不是EventLoop所在线程,那么就将pendingTasks()封装在一个Callable(即,pendingTasksCallable)提交到taskQueue中去执行,并同步的等待执行的结果。
静态代码块
// Workaround for JDK NIO bug.
//
// See:
// - http://bugs.sun.com/view_bug.do?bug_id=6427854
// - https://github.com/netty/netty/issues/203
static {
final String key = "sun.nio.ch.bugLevel";
final String buglevel = SystemPropertyUtil.get(key);
if (buglevel == null) {
try {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
System.setProperty(key, "");
return null;
}
});
} catch (final SecurityException e) {
logger.debug("Unable to get/set System Property: " + key, e);
}
}
int selectorAutoRebuildThreshold = SystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
if (selectorAutoRebuildThreshold < MIN_PREMATURE_SELECTOR_RETURNS) {
selectorAutoRebuildThreshold = 0;
}
SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.noKeySetOptimization: {}", DISABLE_KEYSET_OPTIMIZATION);
logger.debug("-Dio.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD);
}
}