Netty源碼分析第二章: NioEventLoophtml
概述:ide
經過上一章的學習, 咱們瞭解了Server啓動的大體流程, 有不少組件與模塊並無細講, 從這個章開始, 咱們開始詳細剖析netty的各個組件, 並結合啓動流程, 將這些組件的使用場景及流程進行一個詳細的說明函數
這一章主要學習NioEventLoop相關的知識, 何爲NioEventLoop? NioEventLoop是netty的一個線程, 在上一節咱們建立兩個NioEventLoopGroup:oop
EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup();
這裏建立了兩個group, 咱們提過這是boss線程組和worker線程組, 其實這兩個線程組就至關於兩個NioEventLoop的集合, 默認每一個NioEventLoopGroup建立時, 若是不傳入線程數, 會建立cpu核數*2個NioEventLoop線程, 其中boss線程經過輪詢處理Server的accept事件, 而完成accept事件以後, 就會建立客戶端channel, 經過必定的策略, 分發到worker線程進行處理, 而worker線程, 則主要用於處理客戶端的讀寫事件源碼分析
除了輪詢事件, EventLoop線程還維護了兩個隊列, 一個是延遲任務隊列, 另外一個是普通任務隊列, 在進行事件輪詢的同時, 若是隊列中有任務須要執行則會去執行隊列中的任務學習
一個NioEventLoop綁定一個selector用於處理多個客戶端channel, 可是一個客戶端channel只能被一個NioEventLoop處理, 具體關係如圖2-0-1所示:this
2-0-1spa
圖中咱們看到, 一個NioEventLoopGroup下有多個NioEventLoop線程, 而一個線程能夠處理多個channel, 其中有個叫pipeline和handler的東西, 同窗們可能比較陌生, 這是netty的事件傳輸機制, 每一個pipeline和channel惟一綁定, 這裏只須要稍做了解, 以後章節會帶你們詳細剖析線程
瞭解了這些概念, 咱們繼續以小節的形式對NioEventLoop進行剖析netty
第一節: NioEventLoopGroup之建立線程執行器
首先回到第一章最開始的demo, 咱們最初建立了兩個線程組:
EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup();
這裏, 咱們跟隨建立EventLoopGroup的構造方法, 來繼續學習NioEventLoopGroup的建立過程
以workerGroup爲例咱們跟進其構造方法:
public NioEventLoopGroup() { this(0); }
繼續跟進this(0):
public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor) null); }
這裏的nThreads就是剛傳入的0, 繼續跟進:
public NioEventLoopGroup(int nThreads, Executor executor) { this(nThreads, executor, SelectorProvider.provider()); }
這裏nThreads仍然爲0, executor爲null, 這個execute是用於開啓NioEventLoop線程所須要的線程執行器, SelectorProvider.provider()是用於建立selector, 這個以後會講到
咱們一直跟到構造方法最後:
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory, final RejectedExecutionHandler rejectedExecutionHandler) { super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory, rejectedExecutionHandler); }
這裏調用了父類的構造方法
跟進super, 進入了其父類MultithreadEventExecutorGroup的構造方法中:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, chooserFactory, args); }
這裏咱們看到, 若是傳入的線程數量參數爲0, 則會給一個默認值, 這個默認值就是兩倍的CPU核數, chooserFactory是用於建立線程選擇器, 以後會講到, 繼續跟代碼以後, 咱們就看到了建立NioEventLoop的真正邏輯, 在MultithreadEventExecutorGroup類的構造方法中
跟到MultithreadEventExecutorGroup類的構造方法:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { //代碼省略
if (executor == null) { //建立一個新的線程執行器(1)
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } //構造NioEventLoop(2)
children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { boolean success = false; try { children[i] = newChild(executor, args); success = true; } catch (Exception e) { throw new IllegalStateException("failed to create a child event loop", e); } finally { //代碼省略
} } //建立線程選擇器(3)
chooser = chooserFactory.newChooser(children); //代碼省略
}
這邊將代碼主要分爲三個步驟:
1.建立線程執行器
2.建立EventLoop
3.建立線程選擇器
這一小節咱們主要剖析第1步, 建立線程執行器:
這裏有個new DefaultThreadFactory()建立一個DefaultThreadFactory對象, 這個對象做爲參數傳入ThreadPerTaskExecutor的構造函數, DefaultThreadFactory顧名思義, 是一個線程工廠, 用於建立線程的, 簡單看下這個類的繼承關係:
public class DefaultThreadFactory implements ThreadFactory{//類體}
這裏繼承了jdk底層ThreadFactory類, 用於建立線程
咱們繼續跟進該類的構造方法:
protected ThreadFactory newDefaultThreadFactory() { return new DefaultThreadFactory(getClass()); }
其中getClass()就是當前類的class對象, 而當前類是NioEventLoopGroup
繼續跟進到DefaultThreadFactory的構造方法中:
public DefaultThreadFactory(Class<?> poolType) { this(poolType, false, Thread.NORM_PRIORITY); }
poolType是NioEventLoop的class對象, Thread.NORM_PRIORITY是設置默認的優先級爲5
繼續跟構造方法:
public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) { this(toPoolName(poolType), daemon, priority); }
這裏的toPoolName(poolType)是將線程組命名, 這裏返回後結果是"nioEventLoopGroup"(開n頭小寫), daemon爲false, priority爲5
繼續跟構造方法:
public DefaultThreadFactory(String poolName, boolean daemon, int priority) { this(poolName, daemon, priority, System.getSecurityManager() == null ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup()); }
System.getSecurityManager() == null ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup() 這段代碼是經過三目運算建立jdk底層的線程組
繼續跟this():
public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) { //省略驗證代碼 //線程名字前綴
prefix = poolName + '-' + poolId.incrementAndGet() + '-'; this.daemon = daemon; //優先級
this.priority = priority; //初始化線程組
this.threadGroup = threadGroup; }
這裏初始化了DefaultThreadFactory的屬性, prefix爲poolName(也就是nioEventLoopGroup)+'-'+線程組id(原子自增)+'-'
以及初始化了優先級和jdk底層的線程組等屬性
回到最初MultithreadEventExecutorGroup類的構造方法中, 咱們看繼續看第一步:
//建立一個新的線程執行器(1)
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
咱們繼續跟進ThreadPerTaskExecutor的類中:
public final class ThreadPerTaskExecutor implements Executor { private final ThreadFactory threadFactory; public ThreadPerTaskExecutor(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory"); } this.threadFactory = threadFactory; } @Override public void execute(Runnable command) { //起一個線程
threadFactory.newThread(command).start(); } }
咱們發現這個類很是簡單, 繼承了jdk的Executor類, 從繼承關係中我就能猜測到, 而這個類就是用於開啓線程的線程執行器
構造方法傳入ThreadFactory類型的參數, 這個ThreadFactory就是咱們剛纔剖析的DefaultThreadFactory, 這個類繼承了ThreadFactory, 因此在構造方法中初始化了ThreadFactory類型的屬性
咱們再看重寫的 execute(Runnable command) 方法, 傳入一個任務, 而後由threadFactory對象建立一個線程執行該任務
這個execute(Runnable command)方法, 其實就是用開開啓NioEventLoop線程用的, 那麼NioEventLoop何時開啓的, 後面章節會進行剖析
這樣, 經過 executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()) 這種方式就返回了一個線程執行器Executor, 用於開啓NioEventLoop線程