inEventLoop
一直對這個方法不是很理解,衆所周知是判斷當前線程是不是在當前的EventLoop中對應的那個線程?
一個channel對應一個且只對應一個EventLoop,一個Channel對應一個且只對應一個Pipeline,Pipline中包含handler(也是context),當前的Handler被Channel調用,那什麼時候執行這段代碼的線程不是channel對應的那個EventLoop中的線程?
EventExecutor中最關鍵的是inEventLoop方法,用於判斷一個線程是否是EventExecutor內部那個線程,EventExecutor和EventLoop都是單線程實現。inEventLoop的主要使用場景是,當IO變化時,通過channel關聯的pipeline會觸發對應的事件,這些事件對應的執行pipeline中的處理鏈中handler的回調方法,
每個handler添加到pipeline都可以指定自己的EventLoop,如果沒指定,默認使用要添加的pipeline關聯的channel註冊到的EventLoopGroup中的某個EventLoop。
所以channel通過pipeline調用handler時,如果handler沒有單獨指定EventLoop,那inEventLoop就會返回true,他倆由同一個線程處理,直接調用handler。如果handler單獨指定了EventLoop,inEventLoop就會返回false,channel調用handler時就把要調用的方法封裝到Runnable裏,然後添加到handler指定的EventLoop的任務隊列裏,稍後會由對應的EventLoop中的線程執行。
來自:https://www.jianshu.com/p/d39ff4c98c5f
至於代碼分析就不貼代碼了,沒有什麼特別的,關鍵是情況的分析。
自己畫了個圖方便理解,當然肯定還有不對的地方,理解的不是很好,有問題希望提出共同進步!
所以會有下面兩種情況
在handler中:
executor.inEventLoop()?
當前executor是channel對應的那個eventLoop,而inEventLoop中的currentThread是handler自定義的eventLoop,是不相等的。所以是false,false的時候將handler中的回調封裝成task放到自己對應的eventLoop的任務隊列中
這就是異步
當前的channel相當於是主線程,主線程想要回調,發現當前的handler有自己的處理線程,那麼就把回調方法封裝成task給他自己的Executor中的queue中,他自己去執行吧,我該幹嘛幹嘛去了。
爲什麼返回Promise
阿西,實在不想貼代碼
平常用戶代碼習慣這麼寫:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future = ctx.writeAndFlush("test data");
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()){
System.out.println("寫出成功");
}else{
System.out.println("寫出失敗");
}
}
});
}
爲什麼writeAndFlush返回future,下面通過future調用addListener?
在writeAndFlush方法中返回了promise,也就是future
在addListener方法中有一個isDone方法
然後isDone0
判斷result狀態,這個result狀態就是DefaultPromise中的,在writeAndFlush中flush也能修改這個狀態,所以是在writeAndFlush和addListener中相同的。
Promise繼承自Future,Future中有isSuccess方法
所以在operationComplete方法中能夠執行future.isSuccess(),即這個promise(也就是futrure)是writeAndFlush和addListener的一個橋樑,分享着當前是否能夠回調的狀態(是不是寫完了)
這理解其實和JDK JUC中的Future是一個意思了:
promise(也就是futrure)中的result也是一個狀態值,如果writeAndFlush完成了,設置值success,並且去主動
喚醒,或者在addListener中主動
喚醒。
區別就是Future中是通過get方法被動
喚醒的,所以時機可能不是那麼的準確。
另外jdk中的futrue方法是通過get阻塞進行的,runnable通過run生產數據,Future通過get獲取數據,FuturetTask被兩者共享,繼承自他們兩個,擁有了run和get這兩個功能,在自導自演
,run中執行callable的call方法,執行完成設置可執行標誌,沒有完成則標誌還是未完成的,get方法一直阻塞,可是如果已經生產好了,而沒有get呢?所以在Netty中通過listener這種方法更加精準的添加了監聽,能夠第一時間回調。
拾遺
什麼是異步?
是否要等待一個操作的完成?
什麼是阻塞?
是否要等待一個數據準備好?
當然在Netty中還有一個比較有意思的:
WriteAndFlush和addListener方法,如果是異步的,在writeAndFlush之前,完成了AddListener,那麼在WriteAndFlush執行完write有異常回調,或者,執行完flush回調operationComplete的時候,設置爲了Success,是可以放心回調的。
可是如果是兩個方法如果是同步執行,當然異步執行也可能,writeAndFlush執行完了,result設置爲了Success,但是AddListener還沒有添加,那要怎麼回調?operationComplete還沒有準備好呢。通過看源碼能夠知道notifyListenersNow的時候,先判斷listeners(其實就是listener)是不是沒有,沒有直接return了,那不就意味着這次wirteAnfFlush事件沒有回調了? 其實在AddListener方法中,會進行一次判斷,判斷當前的result是不是Success,是的話就會進行一次回調。
代碼分析略。
還有ByteBuf的緩存和命中也應該細細體會。
聲明:
筆者本身還是一個小白,只看到了冰山一角,不一定完全正確,如果文中存在嚴重的錯誤,還請不吝指出,不僅利於自己的修正,同時也減少他人產生對於知識的誤解。