NioEventLoopGroup initialization of netty server

1. Inheritance system diagram of nioeventloopgroup

You can see that the EventExecutorGroup inherits from ScheduleExecutorService and Iterable

This means that there are thread pools and iterators. What does NioEventLoopGroup do to the thread pool and what are iterators iterating? Look down~



2. Function description of eventexecutorgroup core interface

As the core interface, EventExecutorGroup defines the basic core functions. So what does it do?

Official interpretation:

EventExecutorGroup is responsible for providing EventExecutor externally through its own next() method. Moreover, it is also responsible for handling the life cycle of these event executors and providing a global shutdown interface

As can be seen from the following method, EventExecutorGroup itself has the function of thread pool, and then internally maintains a collection of eventexecutors.
EventExecutor itself is inherited from EventExecutorGroup
It can be imagined that EventExecutorGroup is a thread pool group management tool that sets a set of thread pools

method:

/**
* Check whether all eventexecutors managed by EventExecutorGroup have been gracefully closed.
 *  Close all is true	 
*/
boolean isShuttingDown();

/**
 * Simple invocation of the {shutdown gracefully (long, long, timeunit)} method based on the default value
 */
Future<?> shutdownGracefully();

/**
 *Tell the executor that it needs to close. Once this method is called, the isShuttingDown() method starts to return true, and the executor is ready to close itself.
   * Unlike the shutdown() method, this is more elegant. It will have a closed silent period. If a task is submitted during this period, the silent period will be recalculated.
   * In general:
   * It will wait for a certain time before closing. If a task is submitted during this period, the waiting time will restart calculation
 */
Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit);

/**
 * After all eventexecutors managed by the current EventExecutorGroup are closed, asynchronous callback will be performed
 */
Future<?> terminationFuture();

/**
 * This is a forced shutdown and is no longer recommended
 */
@Override
@Deprecated
void shutdown();

/**
 * This is a forced shutdown and is no longer recommended
 * 
 */
@Override
@Deprecated
List<Runnable> shutdownNow();

/**
 * (Important methods)
 *Returns one of the many eventexecutors managed by the EventExecutorGroup. The selection method is determined by the subclass itself
 */
EventExecutor next();

/**
 * =================================================
 * The following are the rewriting of the methods of scheduledexecutorservice and Iterable < eventexecutor > (well, lonely rewriting)
 * =================================================
 */

@Override
Iterator<EventExecutor> iterator();

@Override
Future<?> submit(Runnable task);

@Override
<T> Future<T> submit(Runnable task, T result);

@Override
<T> Future<T> submit(Callable<T> task);

@Override
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

@Override
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

@Override
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

@Override
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);




3. Initialization of nioeventloopgroup

It initializes and turns more. Several default parameters are involved, which is worth thinking about.

It goes through multi-layer construction method calls here, and then enters the parent class construction MultithreadEventLoopGroup, where it makes multiple construction method calls again.

Several construction components are involved:

Executor : new ThreadPerTaskExecutor(newDefaultThreadFactory())

SelectorProvider: the default is SelectorProvider provider()

Selector.select() polling processing policy: defaultselectstrategyfactory INSTANCE

Thread pool queue full policy: rejectedexecutionhandlers reject()

EventExecutor selector: defaulteventexecutorchooserfactory INSTANCE


3.1 what is the default number of threads for NioEventLoopGroup?

io.netty.channel.MultithreadEventLoopGroup

private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
		// The default is cpu processor cores * 2
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    if (logger.isDebugEnabled()) {
        logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}

3.2 core initialization

The following code mainly does two things:

Initialize your own EventExecutor thread pool collection (EventExecutor[] children)

The selector of NioEventLoopGroup for EventExecutor collection (EventExecutorChooserFactory.EventExecutorChooser) is initialized

By the way, we also built a read-only collection. I don't know what to do for the time being. I'll add it when I see it

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        // Check whether nThreads is a positive number. The default is the number of cpu cores * 2
        checkPositive(nThreads, "nThreads");
        // If you do not need a customized thread pool, there is a default one-way thread pool
        if (executor == null) {
            // Thread pool initialization, no task queue
            // newDefaultThreadFactory is responsible for thread creation
            // FastThreadLocalThread
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        // Create thread pool group
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                // Create NioEventLoop as an element of thread pool group management
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                // Todo: think about if this is a good exception type eventexecutorinitializeexception?
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        // Here J < I is very smart. You only need to close the NioEventLoop created before the exception
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                // Waiting to close
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        // The event executor selector means that when a request comes, how does NioEventLoopGroup select the event executor to execute the request
        chooser = chooserFactory.newChooser(children);
        // Terminate the listener and listen to whether the EventExecutor stops
        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        // Read only EventExecutor collection
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }


4. Summary

From the initialization of NioEventLoopGroup, we can see that it is actually used to manage a series of eventexecutors.

The following figure shows the basic component structure of NioEventLoopGroup

Keywords: Java Netty NIO

Added by amos_ on Mon, 03 Jan 2022 10:42:01 +0200