簡介
netty作為一個非同步NIO框架,多執行緒肯定是它的基礎,但是對於netty的實際使用者來說,一般是不需要接觸到多執行緒的,我們只需要按照netty框架規定的流程走下去,自定義handler來處理對應的訊息即可。
那麼有朋友會問了,作為一個NIO框架,netty的多執行緒到底體現在什麼地方呢?它的底層原理是什麼呢?
今天帶大家來看看netty中的任務執行器EventExecutor和EventExecutorGroup。
EventExecutorGroup
因為EventExecutor繼承自EventExecutorGroup,這裡我們先來詳細講解一下EventExecutorGroup。
先看下EventExecutorGroup的定義:
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor>
EventExecutorGroup繼承自JDK的ScheduledExecutorService,即可以執行定時任務,也可以像普通的任務執行器一樣提交任務去執行。
同時EventExecutorGroup還繼承了Iterable介面,表示EventExecutorGroup是可遍歷的,它的遍歷物件是EventExecutor。
EventExecutorGroup有兩個和Iterable相關的方法,分別是next和iterator:
EventExecutor next();
@Override
Iterator<EventExecutor> iterator();
在EventExecutorGroup中呼叫next方法會返回一個EventExecutor物件,那麼EventExecutorGroup和EventExecutor是什麼關係呢?
我們再來看一下EventExecutor的定義:
public interface EventExecutor extends EventExecutorGroup
可以看到EventExecutor實際上是EventExecutorGroup的子類。但是在父類EventExecutorGroup中居然有對子類EventExecutor的引用。
這種在父類的Group中引用返回子類的設計模式在netty中非常常見,大家可以自行體會一下這樣的設計到底是好還是壞。
EventExecutorGroup作為一個EventExecutor的Group物件,它是用來管理group中的EventExecutor的。所以在EventExecutorGroup中設計了一些對EventExecutor的統一管理介面。
比如boolean isShuttingDown()
方法用來判斷這個group中的所有EventExecutor全都在被shutdown或者已經被shutdown。
另外EventExecutorGroupt提供了shutdown group中所有EventExector的方法:Future<?> shutdownGracefully()
和 terminate方法:Future<?> terminationFuture()
。
這兩個方法都返回了Future,所以我們可以認為這兩個方法是非同步方法。
EventExecutorGroup中其他的方法都是一些對JDK中ScheduledExecutorService方法的重寫,比如submit,schedule,scheduleAtFixedRate,scheduleWithFixedDelay等。
EventExecutor
接下來我們再研究一下EventExecutor,在上一節中,我們簡單的提到了EventExecutor繼承自EventExecutorGroup,和EventExecutorGroup相比,EventExecutor有哪些新的方法呢?
我們知道EventExecutorGroup繼承了Iterable,並且定義了一個next方法用來返回Group中的一個EventExecutor物件。
因為Group中有很多個EventExecutor,至於具體返回哪一個EventExecutor,還是由具體的實現類來實現的。
在EventExecutor中,它重寫了這個方法:
@Override
EventExecutor next();
這裡的next方法,返回的是EventExecutor本身。
另外,因為EventExecutor是由EventExecutorGroup來管理的,所以EventExecutor中還存在一個parent方法,用來返回管理EventExecutor的EventExecutorGroup:
EventExecutorGroup parent();
EventExecutor中新加了兩個inEventLoop方法,用來判斷給定的執行緒是否在event loop中執行。
boolean inEventLoop();
boolean inEventLoop(Thread thread);
EventExecutor還提供兩個方法可以返回Promise和ProgressivePromise.
<V> Promise<V> newPromise();
<V> ProgressivePromise<V> newProgressivePromise();
熟悉ECMAScript的朋友可能知道,Promise是ES6引入的一個新的語法功能,用來解決回撥地獄的問題。這裡的netty引入的Promise繼承自Future,並且新增了兩個success和failure的狀態。
ProgressivePromise更進一步,在Promise基礎上,提供了一個progress來表示進度。
除此之外,EventExecutor還提供了對Succeeded的結果和Failed異常封裝成為Future的方法。
<V> Future<V> newSucceededFuture(V result);
<V> Future<V> newFailedFuture(Throwable cause);
EventExecutorGroup在netty中的基本實現
EventExecutorGroup和EventExecutor在netty中有很多非常重要的實現,其中最常見的就是EventLoop和EventLoopGroup,鑑於EventLoop和EventLoopGroup的重要性,我們會在後面的章節中重點講解他們。這裡先來看下netty中的其他實現。
netty中EventExecutorGroup的預設實現叫做DefaultEventExecutorGroup,它的繼承關係如下所示:
<img src="https://img-blog.csdnimg.cn/ae242899a8234b668045716daa2eec1a.png" style="zoom:67%;" />
可以看到DefaultEventExecutorGroup繼承自MultithreadEventExecutorGroup,而MultithreadEventExecutorGroup又繼承自AbstractEventExecutorGroup。
先看下AbstractEventExecutorGroup的邏輯。AbstractEventExecutorGroup基本上是對EventExecutorGroup中介面的一些實現。
我們知道EventExecutorGroup中定義了一個next()方法,可以返回Group中的一個EventExecutor。
在AbstractEventExecutorGroup中,幾乎所有EventExecutorGroup中的方法實現,都是呼叫next()方法來完成的,以submit方法為例:
public Future<?> submit(Runnable task) {
return next().submit(task);
}
可以看到submit方法首先呼叫next獲取到的EventExecutor,然後再呼叫EventExecutor中的submit方法。
AbstractEventExecutorGroup中的其他方法都是這樣的實現。但是AbstractEventExecutorGroup中並沒有實現next()方法,具體如何從Group中獲取到EventExecutor,還需要看底層的具體實現。
MultithreadEventExecutorGroup繼承自AbstractEventExecutorGroup,提供了多執行緒任務的支援。
MultithreadEventExecutorGroup有兩類建構函式,在建構函式中可以指定多執行緒的個數,還有任務執行器Executor,如果沒有提供Executor的話,可以提供一個ThreadFactory,MultithreadEventExecutorGroup會呼叫new ThreadPerTaskExecutor(threadFactory)
來為每一個執行緒構造一個Executor:
protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
this(nThreads, threadFactory == null ? null : new ThreadPerTaskExecutor(threadFactory), args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
MultithreadEventExecutorGroup對多執行緒的支援是怎麼實現的呢?
首先MultithreadEventExecutorGroup提供了兩個children,分別是children和readonlyChildren:
private final EventExecutor[] children;
private final Set<EventExecutor> readonlyChildren;
children和MultithreadEventExecutorGroup中的執行緒個數是一一對應的,有多少個執行緒,children就有多大。
children = new EventExecutor[nThreads];
然後通過呼叫newChild方法,將傳入的executor構造成為EventExecutor返回:
children[i] = newChild(executor, args);
看一下newChild方法的定義:
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
這個方法在MultithreadEventExecutorGroup中並沒有實現,需要在更具體的類中實現它。
readonlyChildren是child的只讀版本,用來在遍歷方法中返回:
readonlyChildren = Collections.unmodifiableSet(childrenSet);
public Iterator<EventExecutor> iterator() {
return readonlyChildren.iterator();
}
我們現在有了Group中的所有EventExecutor,那麼在MultithreadEventExecutorGroup中,next方法是怎麼選擇具體返回哪一個EventExecutor呢?
先來看一下next方法的定義:
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
chooser = chooserFactory.newChooser(children);
public EventExecutor next() {
return chooser.next();
}
next方法呼叫的是chooser的next方法,看一下chooser的next方法具體實現:
public EventExecutor next() {
return executors[(int) Math.abs(idx.getAndIncrement() % executors.length)];
}
可以看到,其實是一個很簡單的根據index來獲取物件的操作。
最後看一下DefaultEventExecutorGroup中對newChild方法的實現:
protected EventExecutor newChild(Executor executor, Object... args) throws Exception {
return new DefaultEventExecutor(this, executor, (Integer) args[0], (RejectedExecutionHandler) args[1]);
}
newChild返回的EventExecutor使用的是DefaultEventExecutor。這個類是EventExecutor在netty中的預設實現,我們在下一小結中詳細進行講解。
EventExecutor在netty中的基本實現
EventExecutor在netty中的預設實現是DefaultEventExecutor,先看下它的繼承結構:
<img src="https://img-blog.csdnimg.cn/67a3434b3dc14e1d98baf8cb5dbf07cf.png" style="zoom:67%;" />
DefaultEventExecutor繼承自SingleThreadEventExecutor,而SingleThreadEventExecutor又繼承自AbstractScheduledEventExecutor,AbstractScheduledEventExecutor繼承自AbstractEventExecutor。
先來看一下AbstractEventExecutor的定義:
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor
AbstractEventExecutor繼承了AbstractExecutorService,並且實現了EventExecutor介面。
AbstractExecutorService是JDK中的類,它提供了 ExecutorService 的一些實現,比如submit, invokeAny and invokeAll等方法。
AbstractEventExecutor作為ExecutorGroup的一員,它提供了一個EventExecutorGroup型別的parent屬性:
private final EventExecutorGroup parent;
public EventExecutorGroup parent() {
return parent;
}
對於next方法來說,AbstractEventExecutor返回的是它本身:
public EventExecutor next() {
return this;
}
AbstractScheduledEventExecutor繼承自AbstractEventExecutor,它內部使用了一個PriorityQueue來儲存包含定時任務的ScheduledFutureTask,從而實現定時任務的功能:
PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue;
接下來是SingleThreadEventExecutor,從名字可以看出,SingleThreadEventExecutor使用的是單執行緒來執行提交的tasks,SingleThreadEventExecutor提供了一個預設的pending執行task的任務大小:DEFAULT_MAX_PENDING_EXECUTOR_TASKS,還定義了任務執行的幾種狀態:
private static final int ST_NOT_STARTED = 1;
private static final int ST_STARTED = 2;
private static final int ST_SHUTTING_DOWN = 3;
private static final int ST_SHUTDOWN = 4;
private static final int ST_TERMINATED = 5;
之前提到了EventExecutor中有一個特有的inEventLoop方法,判斷給定的thread是否在eventLoop中,在SingleThreadEventExecutor中,我們看一下具體的實現:
public boolean inEventLoop(Thread thread) {
return thread == this.thread;
}
具體而言就是判斷給定的執行緒和SingleThreadEventExecutor中定義的thread屬性是不是同一個thread,SingleThreadEventExecutor中的thread是這樣定義的:
這個thread是在doStartThread方法中進行初始化的:
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
所以這個thread是任務執行的執行緒,也就是executor中執行任務用到的執行緒。
再看一下非常關鍵的execute方法:
private void execute(Runnable task, boolean immediate) {
boolean inEventLoop = inEventLoop();
addTask(task);
if (!inEventLoop) {
startThread();
這個方法首先將task新增到任務佇列中,然後呼叫startThread開啟執行緒來執行任務。
最後來看一下DefaultEventExecutor,這個netty中的預設實現:
public final class DefaultEventExecutor extends SingleThreadEventExecutor
DefaultEventExecutor繼承自SingleThreadEventExecutor,這個類中,它定義了run方法如何實現:
protected void run() {
for (;;) {
Runnable task = takeTask();
if (task != null) {
task.run();
updateLastExecutionTime();
}
if (confirmShutdown()) {
break;
}
}
}
在SingleThreadEventExecutor中,我們會把任務加入到task queue中,在run方法中,會從task queue中取出對應的task,然後呼叫task的run方法執行。
總結
DefaultEventExecutorGroup繼承了MultithreadEventExecutorGroup,MultithreadEventExecutorGroup中實際呼叫的是SingleThreadEventExecutor來執行具體的任務。
本文已收錄於 http://www.flydean.com/05-1-netty-event…entexecutorgroup/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!