SoFunction
Updated on 2025-03-06

Netty source code analysis NioEventLoop handles IO events related logic

Previous article Portal:NioEventLoop executes select operation entry

We have learned about the relevant logic of executing the select() operation before. In this section, we continue to learn about the relevant logic of polling the io event:

NioEventLoop run() method:

protected void run() {
    for (;;) {
        try {
            switch ((selectNowSupplier, hasTasks())) {
                case :
                    continue;
                case :
                    //Poll io events (1)                    select((false));
                    if (()) {
                        ();
                    }
                default:
            }
            cancelledKeys = 0;
            needsToSelectAgain = false;
            //The default is 50            final int ioRatio = ; 
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    runAllTasks();
                }
            } else {
                //Record the start time                final long ioStartTime = ();
                try {
                    //Processing the polled key(2)                    processSelectedKeys();
                } finally {
                    // Calculation time                    final long ioTime = () - ioStartTime;
                    //Execute task(3)                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
        //Code omitted    }
}

Let's first look at the judgment if (ioRatio == 100). ioRatio is mainly used to control the ratio of the execution time of the processSelectedKeys() method and the execution time of the task queue. Among them, ioRatio is 50 by default, so it will go to the next step.

First record the start time through final long ioStartTime = () and then process the polled keys through processSelectedKeys() method.

processSelectedKeys() method

private void processSelectedKeys() { 
    if (selectedKeys != null) {
        //flip() method will directly return the key array        processSelectedKeysOptimized(());
    } else {
        processSelectedKeysPlain(());
    }
}

We know that after the selector is optimized through netty, it will initialize the selectedKeys property, so if this property is not empty, it will go to the processSelectedKeysOptimized(()) method. This method is to operate according to the optimized selector.

If it is a non-optimized selector, the processSelectedKeysPlain(()) method will be entered

() is the array bound in selectedKey. We have mentioned in the previous section that selectedKeys are actually stored through arrays, so if you listen to the array of selectedKeys after select() operation, you will have a value if you listen to the event selectedKeys.

processSelectedKeysOptimized(()) method

private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
    //Transfer the array through a for loop    for (int i = 0;; i ++) {
        //Get the current selectionKey        final SelectionKey k = selectedKeys[i];
        if (k == null) {
            break;
        }
        //Set the current reference to null        selectedKeys[i] = null;
        //Get channel(NioSeverSocketChannel)        final Object a = ();
        //If it is AbstractNioChannel, then the processSelectedKey() method is called to handle the io event        if (a instanceof AbstractNioChannel) {
            processSelectedKey(k, (AbstractNioChannel) a);
        } else {
            @SuppressWarnings("unchecked")
            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
            processSelectedKey(k, task);
        }

        //Code omitted    }
}

First, iterate through a for loop through each key in the array. After obtaining the key, first clear the corresponding subscript in the array, because the selector will not be cleared automatically. This is the same as when we use a native selector, when we traversing the set of(), we need to execute remove() after getting the key.

Then obtain the channel registered on the key to determine whether the channel is AbstractNioChannel. Usually, the AbstractNioChannel is AbstractNioChannel, so the processSelectedKey(k, (AbstractNioChannel) a)

processSelectedKey(k, (AbstractNioChannel) a) method

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
    //Get unsafe in channel    final  unsafe = ();
    //If this key is not legal, it means there may be a problem with this channel    if (!()) {
        //Code omitted    }
    try {
        //If it is legal, get the io event of key        int readyOps = ();
        //Link events        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
            int ops = ();
            ops &= ~SelectionKey.OP_CONNECT;
            (ops);
            ();
        }
        //Write events        if ((readyOps & SelectionKey.OP_WRITE) != 0) {
            ().forceFlush();
        }
        //Read events and accept link events        //If the current NioEventLoop is a work thread, this is the op_read event        //If the current NioEventLoop is the boss thread, this is the op_accept event        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            ();
            if (!()) {
                return;
            }
        }
    } catch (CancelledKeyException ignored) {
        (());
    }
}

We first get the unsafe bound to the channel, and then get the channel registration event

We pay attention

 if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) 

I believe this judgment is written in the comments very clearly. If the current NioEventLoop is a work thread, this is the op_read event. If the current NioEventLoop is a boss thread, this is the op_accept event

Then the read() method will be executed through the channel-bound unsafe object to handle links or read and write events

The above is the process of NioEventLoop handling io events. The execution logic of the read() method will be analyzed in detail in future chapters. For more information about Netty NioEventLoop handling IO event logic, please pay attention to my other related articles!