NioEventLoop creation

NioEventLoop creation

NioEventLoop is netty its important components, its primary responsibility is to register on its service channels and found a new connection, read and write occur on these channels I / O event, and then reported the case to channel pipeline processing. When using netty, we first need to do is create NioEventLoopGroup, which is a collection of NioEventLoop, similar to the thread pool thread. Typically, the server will create two group, called bossGroup, called workerGroup. bossGroup responsible for port listening bound to accept the request and create a new connection, after the initialization process subsequent IO referred workerGroup event.

And FIG class NioEventLoop NioEventLoopGroup

First, look at the class diagram of NioEventLoop and NioEventLoopGroup

NioEventLoop
NioEventLoopGroup
Multi-class but chaos, it can be found three characteristics:

  1. Both inherited ExecutorService, so as to establish contact with the thread pool
  2. NioEventLoop inheritance are SingleThread, NioEventLoop inherited MultiThread
  3. NioEventLoop also inherited AbstractScheduledEventExecutor, not difficult to guess this is a task scheduling and timing related to the thread pool

NioEventLoopGroup creation

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

We take a look at the constructor bossGroup and workerGroup.

public NioEventLoopGroup() {
    this(0);
}
public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
}
除此之外,还有多达8种构造方法,这些构造方法可以指定5种参数:
1、最大线程数量。如果指定为0,那么Netty会将线程数量设置为CPU逻辑处理器数量的2倍
2、线程工厂。要求线程工厂类必须实现java.util.concurrent.ThreadFactory接口。如果没有指定线程工厂,那么默认DefaultThreadFactory。
3、SelectorProvider。如果没有指定SelectorProvider,那么默认的SelectorProvider为SelectorProvider.provider()。
4、SelectStrategyFactory。如果没有指定则默认为DefaultSelectStrategyFactory.INSTANCE
5、RejectedExecutionHandler。拒绝策略处理类,如果这个EventLoopGroup已被关闭,那么之后提交的Runnable任务会默认调用RejectedExecutionHandler的reject方法进行处理。如果没有指定,则默认调用拒绝策略。

Finally, NioEventLoopGroup reloads to the parent class constructor MultiThreadEventExecutorGroup omitted here some robustness of the code.

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {
    // 步骤1
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
    
    // 步骤2
    children = new EventExecutor[nThreads];
    for (int i = 0; i < nThreads; i ++) {
        children[i] = newChild(executor, args);
    }    

    // 步骤3
    chooser = chooserFactory.newChooser(children);

    // 步骤4
    final FutureListener<Object> terminationListener = future -> {
        if (terminatedChildren.incrementAndGet() == children.length) {
            terminationFuture.setSuccess(null);
        }
    };
    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }

    // 步骤5
    Set<EventExecutor> childrenSet = new LinkedHashSet<>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

Here can be divided into five steps, explained below step by step

step 1

The first step is to create a thread pool executor. From workerGroup constructor seen, passed in default executor is null, so first create executor. action newDefaultThreadFactory prefix is disposed threads and thread priorities, default prefix naming convention is nioEventLoopGroup-xy, and thread priority is 5, in an intermediate position.
After creating newDefaultThreadFactory, into ThreadPerTaskExecutor. It implements a thread pool of top-level interface juc package, it can be seen from the construction method of the factory simply assigned to their member variables. And it implements the interface method calls newThread method of threadFactory. As the name suggests, it constructs a thread, and the thread start immediately.

public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
    this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
    threadFactory.newThread(command).start();
}    

So newThread way we look back at DefaultThreadFactory found he created a FastThreadLocalThread. This is a netty custom thread class, as the name implies, netty think its faster performance. After about its resolve left. Here in step 1 to create a thread pool is complete. Overall him and we usually use the thread pool is not the same, do not set the thread pool threads and the task queue, but a task to start a thread. (Question: Would not it be more than one task that direct thread explosion?)

@Override
public Thread newThread(Runnable r) {
    Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());        
    return t;
}
protected Thread newThread(Runnable r, String name) {
    return new FastThreadLocalThread(threadGroup, r, name);
}

Step 2

Step 2 is to create workerGroup in NioEventLoop. In the sample code, the number of threads passed in is 0, obviously not really create only 0 nioEventLoop threads. When MultithreadEventLoopGroup call the parent class constructor, a number of thread is determined, if it is 0, the default number of threads is passed, the default value is twice the number of CPU cores

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
// 静态代码块初始化DEFAULT_EVENT_LOOP_THREADS
static {
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads",     NettyRuntime.availableProcessors() * 2));
}    

Next is to create a corresponding NioEventLoop for each EventExecutor by newChild method. This method was introduced to some of the args to NioEventLoop, the first three are passed over when NioEventLoopGroup create a parameter. The default values ​​see above

  1. SlectorProvider.provider, for creating Java NIO Selector objects;
  2. SelectStrategyFactory, plant selection policy;
  3. RejectedExecutionHandlers, refuse execution processor;
  4. EventLoopTaskQueueFactory, task queue factory default is null;

Entering NioEventLoop constructor, as follows:

NioEventLoop构造函数
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
                 EventLoopTaskQueueFactory queueFactory) {
        super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
                rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        }
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }
// 父类构造函数    
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;
    this.executor = ThreadExecutorMap.apply(executor, this);
    this.taskQueue = ObjectUtil.checkNotNull(taskQueue, taskQueue");
    rejectedExecutionHandler = ObjectUtil.checkNotNullrejectedHandler, "rejectedHandler");
}    

NewTaskQueue first calls a method to create a task queue. This is a mpsc that is more than a single producer of consumer lock-free queue. When after calling the parent class constructor, the constructor of the parent class, the NioEventLoopGroup to their parent, and created such a task Executor ---- executed by ThreadPerTaskExecutor passed in through an anonymous inner classes, and in the implementation of the current thread and NioEventLoop binding. Other properties are also set all at once.
In nioEventLoop constructor, we found that created a selector, may wish to take a look at netty its packaging.

unwrappedSelector = provider.openSelector();
if (DISABLE_KEY_SET_OPTIMIZATION) {
    return new SelectorTuple(unwrappedSelector);
}

First seen netty defines a constant DISABLE_KEY_SET_OPTIMIZATION, if this constant is set to true, that is not optimized keyset, the direct return unwrapped selector. So netty to selector which were optimized?

final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {

    SelectionKey[] keys;
    int size;

    SelectedSelectionKeySet() {
        keys = new SelectionKey[1024];
    }
} 

Down, we see a class named selectedSelectionKeySet, you can see into the point, it inherits AbstractSet, but its member variables made us think of the ArrayList, and then look at methods it defines, in addition to not support the remove and outside contains look just like a simplified version of the ArrayList, and even supports expansion.
Yes, Netty indeed by way of reflection, in order to replace selectionKey ArrayList from Set. I think carefully, but feel overwhelmed by the practice of some truth. It is well known, although the time and ArrayList HashSet complexity are found randomly o (1), but compared to the array directly offset positioning, due to the need HashSet Hash operation, time consuming and some a little less. Coupled with the use of the scene, and then traverse the collection are acquired selectionKey, Set deduplication features completely irrelevant, it is no wonder the pursuit of performance netty want to replace it.

Step 3

After creating workerGroup of NioEventLoop, how to choose a nioEventLoop work is a matter of netty want to do next. In general polling is a very easy to think of the program, for which you need to create a similar effect load balancing thread selector. Of course, the pursuit of performance to frenzied netty is not easily satisfied. We look netty in such common scenarios and what had been done in the operation.

public EventExecutorChooser newChooser(EventExecutor[] executors) {
    if (isPowerOfTwo(executors.length)) {
        return new PowerOfTwoEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}
// PowerOfTwo
public EventExecutor next() {
    return executors[idx.getAndIncrement() & executors.length - 1];
}
// Generic
public EventExecutor next() {
    return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}

Can be seen, Netty take two different threads within workerGroup selector according to the number of threads, when x is the number of threads when a power of 2, can be & (x-1) to achieve the effect of the modulo x, other cases require direct access mode. This is forcibly set hashmap capacity of a power of 2, with the same purpose.

Step 4

Step 4 is to add some assurance robustness and add a listener, these listeners will be notified when EventLoop is closed.

Step 5

Create a read-only thread group NioEventLoop

This NioEventLoopGroup and NioEventLoop group contains now created

Guess you like

Origin www.cnblogs.com/spiritsx/p/11900541.html