手记

Netty 源码阅读入门实战(四)-NioEventLoop

1  NioEventLoop概述

  • 总述


2 NioEventLoop创建概述











对应




对应



对应



对应

3 ThreadPerTaskThread

2 服务端Channel的创建





bind 对应样例的


跟进调试






通过反射创建的 channel


看看 channelFactory



  • 反射创建服务端 Channel



    首先




    创建完毕了




  • ANC








4 创建NioEventLoop线程









5 创建线程选择器




  • 先看看普通的



  • 再看幂2的



    循环取数组索引下标,& 比取模性能更高

6 NioEventLoop的启动




对应是




对应是













7 NioEventLoop执行概述

8 检测IO事件


对应的源码为







对应的源码为




执行至此,说明已进行了一次阻塞式的 select 操作


产生空轮询的判断


当空轮询次数大于阈值


阈值定义



阈值



避免空轮询的再次发生


创建新选择器


获取旧选择器的所有 key 值


netty 包装的 channel


将之前的 key 事件解除,并绑定新的选择器和对应的事件


更新选择器的 key


至此,解决了空轮bug

9 处理IO事件



原生JDK创建一个 selector



单线程处理,其实不需要两个数组,后续版本已经是一个数组


只需关注


根本不需要此三个方法



通过反射


即此类



继续反射流程


替换为优化后的 set 集合


一句话总结:用数组替换HashSet 的实现,做到 add 时间复杂度为O(1)






 private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();        if (!k.isValid()) {            final EventLoop eventLoop;            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {                // If the channel implementation throws an exception because there is no event loop, we ignore this
                // because we are only trying to determine if ch is registered to this event loop and thus has authority
                // to close ch.
                return;
            }            // Only close ch if ch is still registerd to this EventLoop. ch could have deregistered from the event loop
            // and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
            // still healthy and should not be closed.
            // See https://github.com/netty/netty/issues/5125
            if (eventLoop != this || eventLoop == null) {                return;
            }            // close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());            return;
        }        try {            int readyOps = k.readyOps();            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
            // the NIO JDK channel implementation may throw a NotYetConnectedException.
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();                if (!ch.isOpen()) {                    // Connection already closed - no need to handle write.
                    return;
                }
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

Netty 默认通过反射将selector 底层的 HashSet 实现转为数组优化
处理每个 ketSet 时都会取到对应的 attachment,即在向 selector注册 io 事件时绑定的经过 Netty 封装后的 channel

10 -reactor线程任务的执行








定时任务






从定时任务中拉取



根据时间,时间相同根据名称


取nanotime 截止时间前的定时任务




从普通 taskqueue 中取任务




作者:芥末无疆sss
链接:https://www.jianshu.com/p/dd9afddaec08
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


0人推荐
随时随地看视频
慕课网APP