“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

Source code analysis

New NioEventLoopGroup(); NioEventLoopGroup (NioEventLoopGroup) : NioEventLoop (NioEventLoop) : NioEventLoop (NioEventLoop) : NioEventLoop (NioEventLoop) : NioEventLoop (NioEventLoop) : NioEventLoop (NioEventLoop) : NioEventLoop (NioEventLoop) Why is he the essence of Netty?

Let’s go straight to NioEventLoop to see its constructor:

In the previous lesson, we created it as a loop filling an array of actuators. For details, see the newChild method in the for loop in the previous lesson. Here is the source code directly

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory queueFactory) {
    // Save the external thread task newTaskQueue(queueFactory)
    super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
          rejectedExecutionHandler);
    this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
    this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
    final SelectorTuple selectorTuple = openSelector();
    this.selector = selectorTuple.selector;
    this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
Copy the code

We’ll catch up on super in a moment

Save the selector producer

 this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
Copy the code

NioEventLoopGroup (NioEventLoopGroup) ¶ NioEventLoopGroup (NioEventLoopGroup) ¶ NioEventLoopGroup (NioEventLoopGroup) ¶

Save the selector

this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
Copy the code

Save a default selection strategy into the NioEventLoop object

Open a selector

final SelectorTuple selectorTuple = openSelector();
Copy the code

Open a selector wrapper object containing a selector! In order to further optimize Netty performance, the official Netty selector is also optimized, we follow the openSelector method, to see how it is optimized, internal code is more complex, we analyze line by line:

1. Get the original selector

unwrappedSelector = provider.openSelector();
Copy the code

Using the original producer object, get an original selector, use later!

2. Determine whether to start optimization with the selector

// Disable optimization options by default false
if (DISABLE_KEY_SET_OPTIMIZATION) {
    // Wrap the original selector if not optimized
    return new SelectorTuple(unwrappedSelector);
}
Copy the code

DISABLE_KEY_SET_OPTIMIZATION defaults to false and returns the selector directly wrapped when optimization is disabled! It is optimized by default, so it is generally not entered into this logic branch!

3. Get the object of a selector’s class

// If you need to optimize
// reflection gets the object SelectorImpl of the corresponding class
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
    @Override
    public Object run(a) {
        try {
            return Class.forName(
                "sun.nio.ch.SelectorImpl".false,
                PlatformDependent.getSystemClassLoader());
        } catch (Throwable cause) {
            returncause; }}});Copy the code

This code is a Class object that returns a SelectorImpl, here is a Class object that returns a SelectorImpl! If an exception fails, it will return an exception. If an exception fails, it will return an exception. If an exception fails, it will return an exception.

 // If no success is obtained
if(! (maybeSelectorImplClassinstanceof Class) ||
    // Make sure the current selector implementation is detectable. It's a subclass or a class of unwrappedSelector! ((Class<? >) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {// An exception occurred
    if (maybeSelectorImplClass instanceof Throwable) {
        Throwable t = (Throwable) maybeSelectorImplClass;
        logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
    }
    // Again wrapped as an unoptimized selector
    return new SelectorTuple(unwrappedSelector);
}
Copy the code

If an exception occurs, or the object retrieved is not the same as the original selector, it is returned with the original selector wrapper!

4. Create an optimized selectKeys

final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
Copy the code

Use NIO should know, using the selector is able to get a Set of events, here Netty official implementation of a Set, internal use of the array to optimize! (this ->Object) (this ->Object) (this ->Object) (this ->Object) (this ->Object) The array is O(1), so Netty uses arrays to optimize the set of selector events. The default is 1024.

You can simply think of it as a Set, except that it is implemented as an array! Internal rewrite add, size, iterator methods, the rest of the methods are scrapped!

This is an important object of Netty’s selector optimization, which makes the algorithm complexity change from O(N) to O(1)!

5. Start replacing selectedKeys with reflection

// Start reflection substitution
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
    @Override
    public Object run(a) {
        try {
            // Get the property object of the event object selectedKeys in the selector event
            Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
            // Get the public selected key
            Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

            // In the case of Java9 and Unsafe, data is directly replaced in memory space
            if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) {
                // Let's try using sun.misc.Unsafe instead of SelectionKeySet.
                // this allows us to do this in Java9 + as well, without any additional flags.
                long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
                long publicSelectedKeysFieldOffset =
                    PlatformDependent.objectFieldOffset(publicSelectedKeysField);

                if(selectedKeysFieldOffset ! = -1&& publicSelectedKeysFieldOffset ! = -1) {
                    PlatformDependent.putObject(
                        unwrappedSelector, selectedKeysFieldOffset, selectedKeySet);
                    PlatformDependent.putObject(
                        unwrappedSelector, publicSelectedKeysFieldOffset, selectedKeySet);
                    return null;
                }
                // If you can't replace the memory space directly, use reflection instead
            }
            // Java8 or Java9 + replace the above unfinished operations with reflection
            Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
            if(cause ! =null) {
                return cause;
            }
            cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
            if(cause ! =null) {
                return cause;
            }
            // Start the substitution with the optimized event array we created reflected into the selector
            selectedKeysField.set(unwrappedSelector, selectedKeySet);
            publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
            return null;
        } catch (NoSuchFieldException e) {
            return e;
        } catch (IllegalAccessException e) {
            returne; }}});Copy the code

Although the code is much, but, the logic is relatively simple!

  1. First of all getSelectorImplThe class objectselectedKeysProperties andpublicSelectedKeysAttributes as well!
  2. Check if you’re using JDK 9 or higher, and if you’re using JAVA’s Unsafe object, navigate directly to the operating system’s memory space! Unsafe, the zero-copy chapter is very detailed. Review the zero-copy chapter! We use JDK8 here
  3. If using JDK8, use reflection to create the optimized object for SelectedKeysSelectedSelectionKeySetReflection of the substitution intounwrappedSelectorThe original selector!

6. Wrap selectors

return new SelectorTuple(unwrappedSelector, new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
Copy the code

First wrap unwrappedSelector selector for SelectedSelectionKeySetSelector wrapper classes!

Then wrap unwrappedSelector and match them with the SelectedSelectionKeySetSelector, Wie tuples returned!

Save the optimized selector and the original selector

this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
Copy the code

Call the parent class to create a queue

super(parent, executor, false, newTaskQueue(queueFactory), 
      newTaskQueue(queueFactory),rejectedExecutionHandler);
Copy the code

First, he builds two queues with newTaskQueue. What are the types of these queues?

QueueFactory == null; DEFAULT_MAX_PENDING_TASKS if not specified, defaults to Integer.MAX, minimum 16! Let’s go to the branch code and see what queue it has created:

public static <T> Queue<T> newMpscQueue(a) {
    return Mpsc.newMpscQueue();
}
Copy the code

It’s a multi-producer, single-consumer queue, provided by the JCTools framework. I’ll talk more about this queue if I can, and we’ll see that when we create NIOEventLoop, we pass two Mpsc queues inside the parent class. Let’s go back to the main line:

Enter the super(XXX) source code:

protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor, boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
    // Save a tailTasks tailqueue
    tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
}
Copy the code

Tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue: Netty tail-queue

We continue to follow the super method source:

Parent thread executor false MPSC queue rejection policy
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, Queue<Runnable> taskQueue,
                                          RejectedExecutionHandler rejectedHandler) {
    super(parent);
    this.addTaskWakesUp = addTaskWakesUp;
    this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
    // Save thread executor
    this.executor = ThreadExecutorMap.apply(executor, this);
    NewTaskQueue (queueFactory) // Create a queue Mpscq to be used by external threads when executing (not within the thread of the EventLoop)
    this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
    // Save the rejection policy
    this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
Copy the code

Save NioEventLoop thread executor, MpscQuerey task queue, and reject policy. Do not feel strange when you see the code using corresponding variables later!

conclusion

  1. Two multi-producer single-consumer queues were created and savedtailTasksandtaskQueue
  2. Save a thread executorexecutor
  3. Save a rejection policy, which is mainly used to deal with the queue when it is full!
  4. Save a selector producer!
  5. Create an optimized selector and save it!
  6. Save the original selector and the optimized one!