分享內容如下:
- 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
//NioEventLoop 覆蓋了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
因爲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) 是以單線程運行的。
小結: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]);
}
}
NioEventLoopGroup只有這個一個覆蓋方法。創建NioEventLoop
MultithreadEventExecutorGroup-next()
@Override
public EventExecutor next() {
return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}
小結: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操作。