源碼地址: GitHub
1.NioEventLoopGroup實例化過程
下面來分析對NioEventLoopGroup類進行實例化的過程中發生了什麼。
NioEventLoopGroup 類層次結構
先給出類圖:
我們查看上面接口的結構圖:
ExecutorService模擬Java線程池接口,ScheduledExecutorService模擬定時器線程池接口。
對於NioEventLoopGroup核心的類繼承關係就是:
NioEventLoopGroup –》MultithreadEventLoopGroup –》MultithreadEventExecutorGroup
面從這三個類出發分析NioEventLoopGroup實例化過程。
首先盜用一下網上的一張示意圖
下面大致解釋一下這個實例化過程做了什麼
//對於不指定線程數參數的構造器,默認設置0 (但是在後面的構造器中會判斷,如果設置爲0 就會初始化爲2*CPU)
public NioEventLoopGroup() {
this(0);
}
/**
* Create a new instance using the specified number of threads, {@link ThreadFactory} and the
* {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
*/
//然後調用:這裏設置了NioEventLoopGroup線程池中每個線程執行器默認是null(這裏設置爲null,在後面的構造器中會判斷,如果爲null就實例化一個線程執行器)
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
//再調用:這裏就存在於JDK的NIO的交互了,這裏設置了線程池的SelectorProvider, 通過SelectorProvider.provider() 返回。
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
//然後調用:在這個重載的構造器中又傳入了默認的選擇策略工廠DefaultSelectStrategyFactory;
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
//這裏就是調用父類MultithreadEventLoopGroup的構造器了, 這裏還添加了線程的拒絕執行策略。
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
在MultithreadEventLoopGroup構造器調用:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
接下來就是調用的基類MultithreadEventExecutorGroup的構造器:
//這個構造器裏面多傳入了一個參數 DefaultEventExecutorChooserFactory.INSTANCE ,
// 通過這個EventLoop選擇器工廠可以實例化GenericEventExecutorChooser或者PowerOfTwoEventExecutorChooser
//這個類, 這個類是EventLoopGroup線程池裏面的EventLoop的選擇器,
//調用GenericEventExecutorChooser.next() 方法可以從線程池中選擇出一個合適的EventLoop線程。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
然後就是重載調用MultithreadEventExecutorGroup類的構造器:
構造器調用到了這裏其實也就是不斷向上傳遞調用的終點了,由於構造器代碼比較長,我就刪除一些校驗和不重要的代碼,只保留核心代碼:
/**
* 最終的創建實例構造器
*
* @param nThreads 該實例將使用的線程數
* @param executor 將要使用的executor, 默認爲null
* @param chooserFactory 將要使用的EventExecutorChooserFactory
* @param args arguments which will passed to each {@link #newChild(Executor, Object...)} call
*/
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
/** 1.初始化線程池 */
//參數校驗nThread合法性,
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
//executor校驗非空, 如果爲空就創建ThreadPerTaskExecutor, 該類實現了 Executor接口
//這個executor 是用來執行線程池中的所有的線程,也就是所有的NioEventLoop,其實從
//NioEventLoop構造器中也可以知道,NioEventLoop構造器中都傳入了executor這個參數。
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//這裏的children數組, 其實就是線程池的核心實現,線程池中就是通過指定的線程數組來實現線程池;
//數組中每個元素其實就是一個EventLoop,EventLoop是EventExecutor的子接口。
children = new EventExecutor[nThreads];
//for循環實例化children數組,NioEventLoop對象
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//newChild(executor, args) 函數在NioEventLoopGroup類中實現了,
// 實質就是就是存入了一個 NIOEventLoop類實例
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
//如果構造失敗, 就清理資源
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
// 2.實例化線程工廠執行器選擇器: 根據children獲取選擇器
chooser = chooserFactory.newChooser(children);
// 3.爲每個EventLoop線程添加 線程終止監聽器
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
// 4. 將children 添加到對應的set集合中去重, 表示只可讀。
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);//(EventLoop是EventExecutor的子接口)
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
總結一下上面的初始化步驟(3)中的一些重點:
1)NIOEventLoopGroup的線程池實現其實就是一個NIOEventLoop數組,一個NIOEventLoop可以理解成就是一個線程。
2)所有的NIOEventLoop線程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是屬於某一個NIOEventLoopGroup的。 這一點從 newChild(executor, args); 方法就可以看出:newChild()的實現是在NIOEventLoopGroup中實現的。
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
3)當有IO事件來時,需要從線程池中選擇一個線程出來執行,這時候的NioEventLoop選擇策略是由GenericEventExecutorChooser實現的, 並調用該類的next() 方法獲取到下一個 NioEventLoop.
到了這裏線程池的初始化就已經結束了, 基本這部分就只涉及 netty線程池的內容,不涉及到channel 與 channelPipeline和ChannelHandler等內容。下面的內容就是分析 NioEventLoop的構造器實現了。
2. NioEventLoop類層次結構
NioEventLoop 的類層次結構圖還是比較複雜的, 不過我們只需要關注幾個重要的點即可. 首先 NioEventLoop 的繼承鏈如下:
NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor
在 AbstractScheduledEventExecutor 中, Netty 實現了 NioEventLoop 的 schedule 功能, 即我們可以通過調用一個 NioEventLoop 實例的 schedule()方法來運行一些定時任務. 而在 SingleThreadEventLoop 中, 又實現了任務隊列的功能, 通過它, 我們可以調用一個 NioEventLoop 實例的 execute() 方法來向任務隊列中添加一個 task, 並由 NioEventLoop 進行調度執行.
通常來說, NioEventLoop 肩負着兩種任務,:
1)第一個是作爲 IO 線程, 執行與 Channel 相關的 IO 操作, 包括 調用 select 等待就緒的 IO 事件、讀寫數據與數據的處理等;
2)而第二個任務是作爲任務隊列, 執行 taskQueue 中的任務, 例如用戶調用 eventLoop.schedule 提交的定時任務也是這個線程執行的.
NioEventLoop 的實例化過程
對於NioEventLoop的實例化,基本就是在NioEventLoopGroup.newChild() 中調用的,下面先給出源碼:
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
從上面函數裏面 new NioEventLoop()出發分析實例化過程:
(1)最先調用NioEventLoop 裏面的構造函數:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
需要注意的是:構造器裏面傳入了 NioEventLoopGroup、Executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler。從這裏可以看出,一個NioEventLoop屬於某一個NioEventLoopGroup, 且處於同一個NioEventLoopGroup下的所有NioEventLoop 公用Executor、SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler。
還有一點需要注意的是,這裏的SelectorProvider構造參數傳入的是通過在NioEventLoopGroup裏面的構造器裏面的 SelectorProvider.provider();方式獲取的, 而這個方法返回的是一個單例SelectorProvider, 所以所有的NioEventLoop公用同一個單例SelectorProvider。
(2)核心的東西說完了,就是調用父類SingleThreadEventLoop的構造器:
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
//父類構造器
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
tailTasks = newTaskQueue(maxPendingTasks);
}
這裏除了調用父類SingleThreadEventExecutor的構造器以外, 就是實例化了 tailTasks 這個變量;
對於tailTasks在SingleThreadEventLoop屬性的定義如下:
private final Queue<Runnable> tailTasks;// 尾部任務隊列
隊列的數量maxPendingTasks參數默認是SingleThreadEventLoop.DEFAULT_MAX_PENDING_TASK,其實就是Integer.MAX_VALUE; 對於new的這個隊列, 其實就是一個LinkedBlockingQueue 無界隊列。
(3)再看調用的父類SingleThreadEventExecutor的構造器:
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super(parent);// 設置EventLoop所屬於的EventLoopGroup
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);//默認是Integer.MAX_VALUE
this.executor = ObjectUtil.checkNotNull(executor, "executor");
taskQueue = newTaskQueue(this.maxPendingTasks);//創建EventLoop的任務隊列, 默認是 LinkedBlockingQueue
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
自此,NioEventLoop的實例化過程已經分析完畢。
3接下來分析ServerBootstrap 的引導過程
ServerBootstrap的繼承結構圖如下: ServerBootstrap中變量負責SocketChannel的生成,AbstractBootstrap負責ServerSocketChannel的生成。
服務端程序代碼如下:(不重要的部分已經省略)
public class BaseServer {
...
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap sbs = new ServerBootstrap()
.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new BaseServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 綁定端口,開始接收進來的連接
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port );
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
...
}
這裏調用時,我們傳入了兩個EventLoopGroup分別叫bossGroup和childGroup。boss這個EventLoopGroup作爲一個acceptor負責接收來自客戶端的請求,然後分發給worker這個EventLoopGroup來處理所有的時間event和channel的IO.
3.1設置EventLoopGroup對象
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
查看上面源碼可知,首先調用super.group() 這個方法調用ServerBootstrap的父類AbstractBootstrap的group(EventLoopGroup group) 方法,下面看看AbstractBootstrap方法的定義:
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return (B) this;
}
3.2 設置ServerBootstrap的channel() : channel(NioServerSocketChannel.class)
實際上是調用的是AbstractBootstrap裏面的channel()函數,下面先粘貼出該方法的源碼:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
這裏傳入的是一個Class對象,根據傳入的不同的Class對象,實例化不同的Channel,主要是有兩種代表NIO和OIO的對象:NioServerSocketChannel和OioServerSocketChannel。在函數體的最後調用了:
channelFactory(new ReflectiveChannelFactory<C>(channelClass));
參數裏面實例化了一個 ReflectiveChannelFactory對象,這個對象實現了ChannelFactory這個接口的。接下來進入這個函數
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
最後把new的 ReflectiveChannelFactory傳遞給了AbstractBootstrap的channelFactory屬性,該屬性定義如下:
private volatile ChannelFactory<? extends C> channelFactory;
3.3 接下來配置ServerBootstrap的childHandler(ChannelHandler childHandler)
該函數的主要作用是設置channelHandler來處理客戶端的請求的channel的IO,這裏我們一般都用ChannelInitializer這個類的實例或則繼承自這個類的實例,我們實現如下:
childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new BaseServerHandler());
};
})
我們ChannelInitializer這個類的繼承圖:
ChannelInitializer繼承自ChannelHandler。 並且這個類其實就是往pipeline中添加了很多的channelHandler,在ServerBootstrap.childHandler(ChannelHandler childHandler)中的實現如下:傳入的childHandler賦值給ServerBootstrap的childHandler屬性。
3.4 接下來配置ServerBootstrap的option(ChannelOption option , T value);
這裏調用父類AbstractBootstrap的option()方法,源碼如下:
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return (B) this;
}
重要代碼是options.put(option,value); 這裏用到了options這個參數,在AbstractBootstrap的定義如下:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
可知是私有變量,而且是一個Map集合,這個變量主要是設置TCP鏈接中的一些可選項,而且這些屬性是作用於每一個連接到服務器被創建的channel.
3.5 然後調用ServerBootstrap的childOption(ChannelOption childOption,T value);源碼如下:
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) {
throw new NullPointerException("childOption");
}
if (value == null) {
synchronized (childOptions) {
childOptions.remove(childOption);
}
} else {
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}
這個函數和option()函數類似,唯一區別是該屬性設定只作用於被acceptor(也就是boss EventLoopGroup)接收之後的channel。
3.6 設置ServerBootstrap的綁定端口和啓動:bind(int port)
前面已經分析完了EventLoopGroup和EventLoop,那麼有一個問題,我們知道一個EventLoop實際上是對應於一個線程,那麼這個EventLoop是什麼時候啓動的呢?
//未完待續