- 默认情况下,Netty服务端起多少线程?何时启动?
- Netty是如何解决jdk空轮询bug的?
- netty如何保证异步串行无锁化?
NioEventLoop
- NioEventLoop创建
- NioEventLoop启动
- NioEventLoop执行逻辑
NioEventLoop创建
1 | new NioEventLoopGroup()[线程数,默认2*cpu] |
跟踪new NioEventLoopGroup()
1 | public NioEventLoopGroup(int nThreads, Executor executor) { |
ThreadPerTaskExcecutor
1 | new ThreadPerTaskExecutor(newDefaultThreadFactory()) |
他会做两件事:为每一次执行任务都创建一个线程实体的工厂,定义NioEventLoop的名字.后期用于创建NioEventLoop线程
每一次执行任务都会创建一个线程实体
ThreadPerTaskExcecutor中包含一个execute方法,代码如下1
2
3
4
public void execute(Runnable command) {
threadFactory.newThread(command).start();//用参数factory每次都创建线程
}NioEventLoop线程命名规则nioEventLoop-1–xx
追一下它的实现方式.首先ThreadPerTaskExecutor的参数的factory:1
2
3
4
5
6
7
8protected ThreadFactory newDefaultThreadFactory() {
return new DefaultThreadFactory(getClass()); //参数为NioEventLoop.class
}
---跟
public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
//toPoolName,把NioEventLoop转成"nioEventLoop"
this(toPoolName(poolType), daemon, priority);
}继续跟构造方法,到
DefaultThreadFactory#DefaultThreadFactory
就会看到1
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
顺便看一下
DefaultThreadFactory#newThread
,就是之前ThreadPerTaskExecutor
里面的那个.就会发现它里面创建了Thread,并且自定义了名字1
Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
newThread
方法源码:
1 | protected Thread newThread(Runnable r, String name) { |
FastThreadLocalThread
继承于Thread,对ThreadLocal做了些优化,并且自己包装了ThreadLocalMap.
newChilld()
接着看在NioEventLoop中做的第二件事:通过for{newChlid()}为每个处理线程(默认cpu*2)创建NioEventLoop.
它主要做这三件事:
- 保存线程执行器ThreadPerTaskExecutor
- 创建一个MpscQueue
- 创建一个selector
1 | newChild(executor, args) // 参数executor就是上面创建的ThreadPerTaskExecutor. |
跟踪到NioEventLoop的构造方法
1 | NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, |
openSelector()就是通过provider.openSelector();
获取轮训器后保存
继续跟踪父类构造方法
1 | protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, |
看一下这个newTaskQueue()
(io.netty.channel.nio.NioEventLoop#newTaskQueue
)
1 |
|
MPSC
: multiProducer(指外部线程)-SingleConsumer (netty的处理线程)
chooserFactory.newChooser()
完成了NioEventLoop[]的创建.接着看在NioEventLoop中做的第三件事:通过chooserFactory.newChooser()构造线程选择器.
目的是给新连接绑定对应的NioEventLoop,通过io.netty.util.concurrent.MultithreadEventExecutorGroup#next
绑定.
他是如何选择线程进行绑定的?很简单,当第一个新连接进来的时候绑定NioEventLoop[]中的第一个,第二个连接则绑定第二个,超过NioEventLoop[]大小后又回到第一个.但netty对此进行了优化:
1 | isPowerOfTwo()[判断NioEventLoop[]的个数是否是2的幂,如2,4,8,16] |
看一下步骤:
1 | chooserFactory.newChooser(children); //参数children就是刚才通过for创建的NioEventLoop的数组 |
关于GenericEventExecutorChooser:
类里面有个next方法:DefaultEventExecutorChooserFactory.GenericEventExecutorChooser#next
就是单纯的executors[Math.abs(idx.getAndIncrement() % executors.length)];
关于PowerOfTowEventExecutorChooser:
类里面有个next方法:DefaultEventExecutorChooserFactory.PowerOfTowEventExecutorChooser#next
返回的是executors[idx.getAndIncrement() & executors.length - 1]