文章目录
- 1. 概述
- 2. EventExecutorGroup实例
- 3. 源码
- 3.1 构造函数
- 3.1.1 DefaultEventLoopGroup 构造函数
- 3.1.2 NioEventLoop 构造函数
- 3.2 run 方法
- 3.2.1 SelectStrategy
- 3.2.2 select
- 3.2.3 processSelectedKeys
- 3.2.3.1 processSelectedKeysOptimized
- 3.2.3.2 processSelectedKeysPlain
- 3.2.3.3 processSelectedKey
- 3.2.3.4 processSelectedKey NioTask
- 3.3 execute
- 3.4 schedule
- 3.5 NioEventLoop register
- 3.6 runAllTasks
- 3.7 wakenUp
1. 概述
EventExecutorGroup 继承了 ScheduledExecutorService、AbstractExecutorService、Iterable;
根据这些名字,大概知道可以提供这些功能 :
- 定时调度线程池定时执行任务
- 线程池异步提交执行任务
- 迭代器
NioEventLoop 继承了 SingleThreadEventLoop,继承了SingleThreadEventExecutor, 是一个单线程事件处理器,单线程事件调度线程池;常用方法:
- NioEventLoop#run 接受处理selector 上的I/O事件,处理普通任务、定时任务
- 注册channel:io.netty.channel.SingleThreadEventLoop#register
- 执行任务:io.netty.util.concurrent.SingleThreadEventExecutor#execute
- 每个EventLoop都有自己的Selector: private Selector selector; private Selector unwrappedSelector;
2. EventExecutorGroup实例
运行截图:
3. 源码
3.1 构造函数
3.1.1 DefaultEventLoopGroup 构造函数
- 可以指定 线程数量 nThreads、线程工厂 threadFactory
- children = new EventExecutor[nThreads]; children[i] = newChild(executor, args); -> new NioEventLoop
- 没有指定 线程数量 nThreads,默认 NettyRuntime.availableProcessors() * 2 ,cpu核心乘2的线程数,或者 io.netty.eventLoopThreads 配置的数量,最小是1
3.1.2 NioEventLoop 构造函数
- newTaskQueue 创建任务队列 Math.max(16, SystemPropertyUtil.getInt(“io.netty.eventLoop.maxPendingTasks”, Integer.MAX_VALUE));
- 创建 NioEventLoop 创建对应的 selector: openSelector()
- args 从创建 MultithreadEventExecutorGroup 的时候带进来
3.2 run 方法
- 轮询 I/O 事件: select(wakenUp.getAndSet(false));
轮询 Selector 选择器中已经注册的所有 Channel 的 I/O 事件
- 处理 I/O 事件: processSelectedKeys(); 处理已经准备就绪的 I/O 事件;
- 处理完 I/O 事件,再处理异步任务队列: runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
ioRatio 参数用于调整 I/O 事件处理和任务处理的时间比例。
3.2.1 SelectStrategy
SelectStrategy 定义:
int SELECT = -1;
int CONTINUE = -2;
int BUSY_WAIT = -3;
- 没有任务返回 SelectStrategy.SELECT
- selectNowSupplier selectNow() 异常返回-1 也是 SelectStrategy.SELECT
- selectNowSupplier selectNow() 异常返回0 或者 数量,走到 default
3.2.2 select
- delayNanos(currentTimeNanos) 没有调度任务返回1s,有调度任务返回调度任务的待执行时间
- 0.5ms内有定时任务需要执行,退出无限循环 : long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
- Netty 的任务队列包括普通任务、定时任务以及尾部任务,hasTask() 判断的是普通任务队列和尾部队列是否为空,而 delayNanos(currentTimeNanos) 方法获取的是定时任务的延迟时间。
- 有待执行任务,wakenUp设置为true,selectNow然后结束
- hasTasks() && wakenUp.compareAndSet(false, true)
- 阻塞等待获取I/O事件
- int selectedKeys = selector.select(timeoutMillis);
- select次数 selectCnt++
- selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks() 都停止 select
- selectedKeys != 0:有io事件待处理
- 参数oldWakenUp为true
- wakenUp为true
- hasTasks 有待执行任务
- hasScheduledTasks 有待调度执行的任务
- Thread.interrupted(), 线程被中断,break
- selector.select(timeoutMillis) 真正等待时间 + 程序运行几毫秒 >= timeoutMillis 预计等待时间,说明selector没有问题,所以selectCnt重置为1
- time [当前时间 = 开始时间 + 真正等待时间 + 程序运行几毫秒(忽略)] - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) [预计等待时间] >= currentTimeNanos [开始时间]
==> 真正等待时间 + 程序运行事件几毫秒(忽略) >= 预计等待时间
- selectCnt 超过一定次数,可能触发了 epoll 空轮询 Bug,重新构建 selector
- SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD
- 重新构建 selector,selector = selectRebuildSelector(selectCnt);
3.2.3 processSelectedKeys
处理已经就绪的 SelectionKey
Netty 优化过的 selectedKeys 是 SelectedSelectionKeySet 类型,而正常逻辑使用的是 JDK HashSet 类型
3.2.3.1 processSelectedKeysOptimized
用的 SelectedSelectionKeySet selectedKeys
SelectedSelectionKeySet 内部使用的是 SelectionKey 数组,所以 processSelectedKeysOptimized 可以直接通过遍历数组取出 I/O 事件,相比 JDK HashSet 的遍历效率更高
3.2.3.2 processSelectedKeysPlain
- 处理I/O事件
- processSelectedKey(k, (AbstractNioChannel) a);
- 处理NioTask
- NioTask task = (NioTask) a;
- processSelectedKey(k, task);
3.2.3.3 processSelectedKey
NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, AbstractNioChannel)
- k.isValid() 检查key是否有效
- ((readyOps & SelectionKey.OP_CONNECT) != 0) :unsafe.finishConnect(); 接受连接
- ((readyOps & SelectionKey.OP_WRITE) != 0): ch.unsafe().forceFlush(); 发送数据到客户端
- ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0):unsafe.read(); 读取客户端数据
3.2.3.4 processSelectedKey NioTask
NioTask 是用户自定义的 task
3.3 execute
- addTask(task); 添加任务到 mpsc无锁队列
- inEventLoop 如果为 false 表示由其它线程来调用 execute,启动线程,STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED),CAS保证不会重复启动
- wakeup 唤醒 selector 的 select 阻塞
io.netty.channel.nio.NioEventLoop#wakeup
3.4 schedule
AbstractScheduledEventExecutor#schedule(io.netty.util.concurrent.ScheduledFutureTask)
scheduledTaskQueue
3.5 NioEventLoop register
- io.netty.channel.nio.NioEventLoop#register
- io.netty.channel.nio.NioEventLoop#register0
- java.nio.channels.spi.AbstractSelectableChannel#register 调用nio进行注册
3.6 runAllTasks
- fetchFromScheduledTaskQueue : scheduledTaskQueue 移动到 taskQueue
- pollTask() : taskQueue 取出任务
- afterRunningAllTasks : taskQueue 为空,执行 tailTasks
- safeExecute:try catch 的 执行 task,有异常不会终止
3.7 wakenUp
进入 select ,设置 wakenUp 为 false
io.netty.channel.nio.NioEventLoop#wakeup
io.netty.util.concurrent.SingleThreadEventExecutor#execute
提交任务会 wakeup(inEventLoop);