“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!
- First of all get
SelectorImpl
The class objectselectedKeys
Properties andpublicSelectedKeys
Attributes as well! - 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
- If using JDK8, use reflection to create the optimized object for SelectedKeys
SelectedSelectionKeySet
Reflection of the substitution intounwrappedSelector
The 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
- Two multi-producer single-consumer queues were created and saved
tailTasks
andtaskQueue
- Save a thread executor
executor
- Save a rejection policy, which is mainly used to deal with the queue when it is full!
- Save a selector producer!
- Create an optimized selector and save it!
- Save the original selector and the optimized one!