netty源碼分析5-NioEventLoopGroup

分享內容如下:

  1. NioEventLoopGroup初始化分析
  2. NioEventLoopGroup父子類分析
  3. 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操作。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章