概述
创建过程主要有以下4步:
- 创建NioEventLoopGroup
- 创建ThreadPerTaskExecutor
- 创建NioEventLoop
- 创建chooser
下面对这四个步骤进行详细解析
1. 创建NioEventLoopGroup
NioEventLoopGroup创建.png如上图构造器的调用过程,其中重点关注几个参数的默认值
- selectorProvider:根据操作系统返回不同的provider
- selectStrategyFactory:默认DefaultSelectStrategyFactory,后续run()中select时会用到(有task就通过selectNow取,没有返回-1)
- nThread:NioEventLoop数组size(也就是线程数)为空时默认为当前cpu核数的两倍
- chooserFactory:默认DefaultEventExecutorChooserFactory,后续通过该factory获取选择器(从NioEventLoop数组中选择一个跟新连接进行绑定)
-
args可变参数:
第一个参数selectorProvider;
第二个参数selectStrategyFactory
第三个参数(拒绝策略)默认RejectedExecutionHandler(抛出
RejectedExecutionException异常)
2/3/4步代码如下(忽略了非主流程代码)
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
if (executor == null) {
//2. 创建ThreadPerTaskExecutor
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//初始化大小为nThreads数组(NioEventLoop数组)
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//3. 创建NioEventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
//忽略
}
}
//4. 创建chooser
chooser = chooserFactory.newChooser(children);
}
2. 创建ThreadPerTaskExecutor
ThreadPerTaskExecutor 每次执行任务都会创建一个线程实体; 通过ThreadFactory进行构造,然后每次execute都会创建一个线程去运行
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
//构造方法传入threadFactory 即 newDefaultThreadFactory()返回
this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
}
@Override
public void execute(Runnable command) {
//该执行器execute时,即通过threadFactory创建一个thread,然后start
threadFactory.newThread(command).start();
}
}
newDefaultThreadFactory(),线程工厂,通过newThread创建线程
public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
//默认线程名:nioEventLoopGroup-group序号(从1自增)-线程号(从0自增)
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
//是否守护线程:false
this.daemon = daemon;
//优先级:10
this.priority = priority;
this.threadGroup = threadGroup;
}
protected Thread newThread(Runnable r, String name) {
//创建FastThreadLocalThread
return new FastThreadLocalThread(threadGroup, r, name);
}
- ThreadPerTaskExecutor:执行器实现jdk Executor接口
- DefaultThreadFactory:创建执行线程的工厂,创建出FastThreadLocalThread,FastThreadLocalThread继承自jdk Thread,netty为了提高threadLocal性能设计,后续单独详解
3. 创建NioEventLoop
创建NioEventLoop放到数组中,新连接建立时会从数组中选取一个NioEventLoop进行绑定
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory queueFactory) {
//newTaskQueue创建MpscQueue(一个消费者多个生产者的队列)
super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory), rejectedExecutionHandler);
//SelectorProvider.provider()
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
//DefaultSelectStrategy
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
//调用jdk openSelector方法打开一个选择器selector
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
- parent:NioEventLoopGroup
- executor: ThreadPerTaskExecutor,第二步创建
- selectorProvider:SelectorProvider.provider(),第一步初始化
- strategy:DefaultSelectStrategy,第一步初始化
- rejectedExecutionHandler:RejectedExecutionHandlers,第一步初始化
- queueFactory:默认null
- newTaskQueue:创建MpscQueue队列(后续详细讲解)
4. 创建chooser
后续新连接通过该选择器找到需要绑定的NioEventLoop
public EventExecutorChooser newChooser(EventExecutor[] executors) {
//nThread是否 2 的倍数
if (isPowerOfTwo(executors.length)) {
//经过优化的方式获取数组下标:idx.getAndIncrement() & executors.length - 1
return new PowerOfTwoEventExecutorChooser(executors);
} else {
//普通方式获取数组下标:Math.abs(idx.getAndIncrement() % executors.length)
return new GenericEventExecutorChooser(executors);
}
}
- isPowerOfTwo:数组长度是否2的倍数
- 是2的倍数:会通过优化的方式获取下标(通过位运算),性能更好;
-
不是2倍数:普通方式获取下标(直接取模)
两种方式最终达到的效果都是顺序依次获取,只不过2的倍数时可以通过位运算提升性能;
网友评论