分享内容如下:
- NioEventLoopGroup初始化分析
- NioEventLoopGroup父子类分析
- parentGroup与childGroup区别
1.NioEventLoopGroup初始化分析
从接口类图上看 EventLoopGroup 继承了 线程池和定时线程池功能,判断EventLoopGroup具有线程池的复合功能。从命名猜测 EventLoopGroup 管理了一组 EventLoop
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
//.......
//executor默认初始化类型为ThreadPerTaskExecutor
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//创建了length=8的EventExecutor组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 返回NioEventLoop
//NioEventLoopGroup 覆盖了newChild方法
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
}
}
}
ThreadPerTaskExecutor分析
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//使用了新的ThreadFactory,为生成的线程增加线程名前缀。
线程名前缀规则: 类简名+“-”+ThreadFactory序号+“-”
如: nioEventLoopGroup-2-
后缀:从1开始每创建一次加入
完整线程名如下
nioEventLoopGroup-2-1
nioEventLoopGroup-2-2
nioEventLoopGroup的ThreadFactory序号是2
生成的线程 toString:Thread[nioEventLoopGroup-2-1,10,main] 其中nioEventLoopGroup-2-1是线程名,10是优先级策略,main是组名默认生成
因为io.netty.util.concurrent.GlobalEventExecutor 提前加载,其中字段调用了一次DefaultThreadFactory构造方法。
代码:private final ThreadFactory threadFactory = new DefaultThreadFactory(getClass());
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
//threadFactory赋值
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
@Override
//使用threadFactory创建线程
public void execute(Runnable command) {
threadFactory.newThread(command).start(); //生成新线程,并以单线程执行 command
}
}
这里需要 注意 ThreadPerTaskExecutor.execute(Runnable command) 是以单线程运行的。NioEventLoop启动 调用了这个方法。
小结:ThreadPerTaskExecutor使用了新的ThreadFactory,为生成的线程增加线程名前缀。
实现了创建新线程,并以单线程执行 Runnable 的execute方法。
EventLoopGroup 中重要的两个方法,newChild()和next(),代码如下。
NioEventLoopGroup-newChild()
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0]);
}
}
newChild:创建NioEventLoop
NioEventLoopGroup只有这个一个覆盖方法。创建NioEventLoop
MultithreadEventExecutorGroup-next()
@Override
public EventExecutor next() {
return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}
next:轮询获取从Reactor线程组的NioEventLoop
小结:NioEventLoopGroup初始化最主要的任务是创建了NioEventLoop数组。
2.NioEventLoopGroup父子类分析
主要的接口类图如下
EventExecutorGroup:next 获取EventExecutor, 线程任务执行, 定时任务
EventExecutor:多了newPromise,inEventLoop();
EventLoopGroup:只有EventLoop next();
EventLoop:只有以下3个方法
EventLoopGroup parent();//暂时不分析
EventLoop next();//看了其实现类 都是return this
ChannelHandlerInvoker asInvoker();//返回一个包装handler执行的invoker,默认使用
分析NioEventLoopGroup中的父子类
NioEventLoopGroup类图如下
AbstractEventExecutorGroup:next 获取EventExecutor未实现。线程任务执行, 定时任务都是 next().xxx()
MultithreadEventExecutorGroup:类注释的意思是多线程方式处理任务,从代码实现上来看,没有多线程的处理,主要功能是维护EventExecutor数组,实现了next()。
主要方法如下
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
//.......
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//创建size()=nThreads的线程组
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
} finally {
//......
}
}
//重要:实现了next获取EventExecutor
public EventExecutor next() {
return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}
//由子类实现
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
MultithreadEventLoopGroup:类注释无参照意义。主要功能是获取默认的线程数,/重写next返回类型为EventLoop
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
//获取默认的线程数
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
}
//重写next返回类型为EventLoop
public EventLoop next() {
return (EventLoop) super.next();
}
newChild 还是没有实现
NioEventLoopGroup:
主要方法如下
//设置 希望child event loops 占用IO线程执行与执行任务的百分比。默认值是50, 此时I/O 和 non-I/O 将会试图占用相同的时间。
public void setIoRatio(int ioRatio) {
for (EventExecutor e: children()) {
((NioEventLoop) e).setIoRatio(ioRatio);
}
}
//替换原来的Selector,处理著名的epoll 100% bug,依赖NioEventLoop实现
public void rebuildSelectors() {
for (EventExecutor e: children()) {
((NioEventLoop) e).rebuildSelector();
}
}
//重写 newChild,创建NioEventLoop
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0]);
}
小结:NioEventLoopGroup主要功能是维护EventExecutor数组,实现了next(),暴露了一些线程,工具类方法, 都是依赖NioEventLoop实现。
3.parentGroup与childGroup区别
parentGroup是Reactor模式中主Reactor线程组,childGroup是从Reactor线程组。
怎么区分,当客户端发起连接时,服务需要获取相应的连接与之通讯,此时childGroup发挥作用。
这时会调用AbstractNioMessageChannel$NioMessageUnsafe.read() ,它理解 5.0.0版本parentGroup和childGroup的重要点
public void read() {
//...............................
try {
for (;;) {// 此处用到了循环,有可能 同时有2个以上的客户端发起连接
int localRead = doReadMessages(readBuf);代码A
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
if (readBuf.size() >= maxMessagesPerRead | !autoRead) {
break;
}
}
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
pipeline.fireChannelRead(readBuf.get(i));代码B
}
readBuf.clear();
pipeline.fireChannelReadComplete();
//..............................................
}
代码A获取原生SocketChannel,使用childGroup中EventLoop 后创建了NioSocketChannel,使用。
代码B触发ChannelRead事件,最后执行了注册,启动了NioSocketChannel的EventLoop 线程。
这个方法中 获取并启动了 childGroup中的NioEventLoop
小结:server端在接受连接后创建了NioSocketChannel,注册在ChildGroup上,由此可推测,parentGroup负责接受客户端链接,childGroup负责IO操作。
总结:NioEventLoopGroup 管理了一组 NioEventLoop,parentGroup负责接受客户端链接,childGroup负责IO操作。