Netty NioEventLoop执行逻辑

NioEventLoop 执行逻辑

方法: NioEventLoop.run()
for(;; )
select() 检查是否有 IO 事件
processSelectedKeys() 处理上面检测出来的 IO 事件
runAllTasks() 处理异步任务队列

protected void run() {
    for (;;) {
        try {
            try {
                // 1. 调用 select() 查询是否有就绪的 I/O 事件
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;

                case SelectStrategy.BUSY_WAIT:
                    // fall-through to SELECT since the busy-wait is not supported with NIO

                case SelectStrategy.SELECT:
                    select(wakenUp.getAndSet(false)); // select 操作, 设置唤醒状态为 false(要进行 select 操作, 且不为唤醒状态)

                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                    // fall through
                default:
                } // 1. 结束
            }

            // 2. 处理上面检测出来的 IO 事件
            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio; // ioRatio 用于控制 selectedKeys 和 tasks 的执行时间. 默认值 50, 处理 selectedKeys 和 tasks 时间相同
            if (ioRatio == 100) {
                try {
                    processSelectedKeys(); // 处理就绪的 I/O 事件
                } finally {
                    // Ensure we always run tasks.
                    runAllTasks(); // 执行外部线程放入任务队列中的任务
                }
            } else {
                final long ioStartTime = System.nanoTime(); // 记录初始时间
                try {
                    processSelectedKeys(); // 处理就绪的 I/O 事件
                } finally {
                    // Ensure we always run tasks.
                    final long ioTime = System.nanoTime() - ioStartTime;
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio); // 给定时间内执行任务
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
}

select() 逻辑

方法: io.netty.channel.nio.NioEventLoop#select
计算截止时间, 时间不够, selectNow() 并返回
此时任务队列中有任务, selectNow() 并返回
阻塞式 select(截止时间没到, 并且任务队列为空, 则执行该步骤)
避免 jdk 空轮询 bug(销毁原有的 selector, 重建 selector, 将 attachment 和 op 注册到新的 selector 上)

private void select(boolean oldWakenUp) throws IOException {
    Selector selector = this.selector;
    try {
        int selectCnt = 0;
        long currentTimeNanos = System.nanoTime(); // 取当前时间
        long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos); // selectDeadLineNanos 指可以进行 select 操作的截止时间点

        for (;;) {
            long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L; // 四舍五入将 select 操作时间换算为毫秒单位
            if (timeoutMillis <= 0) { // 时间不足 1ms, 不再进行 select 操作
                if (selectCnt == 0) { // 如果一次 select 操作没有进行
                    selector.selectNow(); // selecNow() 之后返回
                    selectCnt = 1;
                }
                break; // 循环出口
            }

            // ...
            if (hasTasks() && wakenUp.compareAndSet(false, true)) { // 此时有任务进入队列 (任务队列中有任务需要执行) 且 唤醒标志为假 (并 cas 设置为 true)
                selector.selectNow(); // selectNow() 返回,否则会耽误任务执行
                selectCnt = 1;
                break; // 循环出口
            }

            int selectedKeys = selector.select(timeoutMillis); // 阻塞式 select
            selectCnt ++; // select 操作计数 ++
            // 有就绪的 IO 事件, 参数 oldWakenUp 为真, 外部设置 wakenUp 为真, 有待执行普通任务, 有待执行调度任务
            if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
                // - Selected something,
                // - waken up by user, or
                // - the task queue has a pending task.
                // - a scheduled task is ready for processing
                break; // 循环出口
            }
            if (Thread.interrupted()) {
                // Thread was interrupted so reset selected keys and break so we not run into a busy loop.
                // As this is most likely a bug in the handler of the user or it's client library we will
                // also log it.
                //
                // See https://github.com/netty/netty/issues/2426
                if (logger.isDebugEnabled()) {
                    logger.debug("Selector.select() returned prematurely because" +
                            "Thread.currentThread().interrupt() was called. Use" +
                            "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");
                }
                selectCnt = 1;
                break;
            }

            long time = System.nanoTime(); // 记录当前时间
            if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) { // 截止时间已到, 说明上面的阻塞式 select(timeout) 没有问题
                // timeoutMillis elapsed without anything selected.
                selectCnt = 1;
            } else if (SELECTOR_AUTO_REBUILD_THRESHOLD> 0 &&
                    selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { // 走到这说明发生空轮询 bug 了, 空轮询次数大于阈值
                // The code exists in an extra method to ensure the method is not too big to inline as this
                // branch is not very likely to get hit very frequently.
                selector = selectRebuildSelector(selectCnt); // 重建 select, 避免 jdk 空轮询 bug
                selectCnt = 1;
                break;
            }

            currentTimeNanos = time;
        }

        if (selectCnt> MIN_PREMATURE_SELECTOR_RETURNS) {
            if (logger.isDebugEnabled()) {
                logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
                        selectCnt - 1, selector);
            }
        }
    } catch (CancelledKeyException e) {
        if (logger.isDebugEnabled()) {
            logger.debug(CancelledKeyException.class.getSimpleName() + "raised by a Selector {} - JDK bug?",
                    selector, e);
        }
        // Harmless exception - log anyway
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlyingZCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值