Netty:ChannelFuture

上一篇咱們完成了對Channel的學習,這一篇讓咱們來學習一下ChannelFuture。html

ChannelFuture的簡介編程

ChannelFuture是Channel異步IO操做的結果。異步

Netty中的全部IO操做都是異步的。這意味着任何IO調用都將當即返回,而不能保證所請求的IO操做在調用結束時完成。相反,將返回一個帶有ChannelFuture的實例,該實例將提供有關IO操做的結果或狀態的信息。ide

ChannelFuture要麼是未完成狀態,要麼是已完成狀態。IO操做剛開始時,將建立一個新的Future對象。新的Future對象最初處於未完成的狀態,由於IO操做還沒有完成,因此既不會執行成功、執行失敗,也不會取消執行。若是IO操做由於執行成功、執行失敗或者執行取消致使操做完成,則將被標記爲已完成的狀態,並帶有更多特定信息,例如失敗緣由。請注意,即便執行失敗和取消執行也屬於完成狀態。post

 

ChannelFuture提供了各類方法,可以讓您檢查IO操做是否已完成,等待完成以及獲取IO操做的結果。它還容許您添加ChannelFutureListener,以便在IO操做完成時獲得通知。性能

Prefer addListener(GenericFutureListener) to await()學習

推薦使用addListener(GenericFutureListener)而不是await(),以便在完成IO操做並執行任何後續任務時獲得通知。spa

addListener(GenericFutureListener)是非阻塞的。它只是將指定的ChannelFutureListener添加到ChannelFuture,而且與未來關聯的IO操做完成時,IO線程將通知監聽器。ChannelFutureListener徹底不阻塞,所以可產生最佳的性能和資源利用率,可是若是不習慣事件驅動的編程,則實現順序邏輯可能會比較棘手。線程

相反,await()是阻塞操做。一旦被調用,調用者線程將阻塞直到操做完成。使用await()實現順序邏輯比較容易,可是調用者線程會沒必要要地阻塞直到完成IO操做爲止,而且線程間通知的成本相對較高。此外,在特定狀況下還可能出現死鎖。 code

Do not call await() inside ChannelHandler

ChannelHandler中的事件處理程序方法一般由IO線程調用,若是await()是由IO線程調用的事件處理程序方法調用的,則它正在等待的IO操做可能永遠不會完成,由於await()會阻塞它正在等待的IO操做,這是一個死鎖。

// BAD - NEVER DO THIS
   @Override
   public void channelRead(ChannelHandlerContext ctx, Object msg) {
       ChannelFuture future = ctx.channel().close();
       future.awaitUninterruptibly();
       // Perform post-closure operation
       // ...
   }
// GOOD
   @Override
   public void channelRead(ChannelHandlerContext ctx, Object msg) {
       ChannelFuture future = ctx.channel().close();
       future.addListener(new ChannelFutureListener() {
           public void operationComplete(ChannelFuture future) {
               // Perform post-closure operation
               // ...
           }
       });
   }

 

儘管有上述缺點,可是在某些狀況下,調用await()更方便。在這種狀況下,請確保不要在IO線程中調用await()。 不然,將引起BlockingOperationException來防止死鎖。

 Do not confuse I/O timeout and await timeout 

使用await(long),await(long,TimeUnit),awaitUninterruptible(long)或awaitUninterruptible(long,TimeUnit)指定的timeout與IO超時根本不相關。 若是IO操做超時,則Future將被標記爲「Completed with failure」,如上圖所示。 例如,應經過特定於傳輸的選項配置鏈接超時:

// BAD - NEVER DO THIS
   Bootstrap b = ...;
   ChannelFuture f = b.connect(...);
   f.awaitUninterruptibly(10, TimeUnit.SECONDS);
   if (f.isCancelled()) {
       // Connection attempt cancelled by user
   } else if (!f.isSuccess()) {
       // You might get a NullPointerException here because the future
       // might not be completed yet.
       f.cause().printStackTrace();
   } else {
       // Connection established successfully
   }
  
   // GOOD
   Bootstrap b = ...;
   // Configure the connect timeout option.
   b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
   ChannelFuture f = b.connect(...);
   f.awaitUninterruptibly();
  
   // Now we are sure the future is completed.
   assert f.isDone();
  
   if (f.isCancelled()) {
       // Connection attempt cancelled by user
   } else if (!f.isSuccess()) {
       f.cause().printStackTrace();
   } else {
       // Connection established successfully
   }

 

ChannelFuture的方法

 

 

ChannelFuture的方法並很少,能夠簡單的看一下。

channel():返回ChannelFuture關聯的Channel;

addListener():將指定的listener添加到Future。Future完成時,將通知指定的listener。若是Future已經完成,則當即通知指定的listener;

addListeners():和上述方法同樣,只不過此方法能夠新增一系列的listener;

removeListener():從Future中刪除第一次出現的指定listener。完成Future時,再也不通知指定的listener。若是指定的listener與此Future沒有關聯,則此方法不執行任何操做並以靜默方式返回。

removeListeners():和上述方法同樣,只不過此方法能夠移除一系列的listener;

sync():等待Future直到其完成,若是這個Future失敗,則拋出失敗緣由;

syncUninterruptibly():不會被中斷的sync();

await():等待Future完成;

awaitUninterruptibly():不會被中斷的await ();

isVoid():若是此ChannelFuture是void的Future,則返回true,所以不容許調用如下任何方法:

addListener(GenericFutureListener)

addListeners(GenericFutureListener[])

await()

await(long, TimeUnit) ()}

await(long) ()}

awaitUninterruptibly()

sync()

syncUninterruptibly()

 

爲何使用ChannelFuture?

從JDK1.5以後,J.U.C提供了Future類,它表明着異步計算的結果。Future類提供了以下方法:

 

方法

方法說明

boolean cancel(boolean mayInterruptIfRunning)

嘗試取消執行此任務。若是任務已經完成,已經被取消或因爲某些其餘緣由而沒法取消,則此嘗試將失敗。若是成功,而且在調用cancel時此任務還沒有啓動,則該任務永遠不要運行。若是任務已經啓動,則mayInterruptIfRunning參數肯定是否應中斷執行該任務的線程以嘗試中止該任務。

此方法返回後,對isDone的後續調用將始終返回true。若是此方法返回true,則隨後對isCancelled的調用將始終返回true。

boolean isCancelled()

若是此任務在正常完成以前被取消,則返回true。

boolean isDone()

若是此任務完成,則返回true。完成多是因爲正常終止,異常或取消引發的,在全部這些狀況下,此方法都將返回true。

V get()

必要時等待計算完成,而後檢索其結果。

V get(long timeout, TimeUnit unit)

必要時最多等待給定時間以完成計算,而後檢索其結果。

從這些方法中,能夠看出Future類存在2大問題: 

一、isDone()的定義模糊不清,不論是失敗、異常仍是成功,isDone()返回的都是true;

二、get()獲取結果的方式是阻塞等待的方式。 

因此Netty中的Future對JDK中的Future作了擴展,而ChannelFuture繼承Future,當然也能充分利用這個擴展出的新特性。新特性主要體如今以下兩方面:

一、引入isSuccess()來表示執行成功,引入cause()來表示執行失敗的緣由;

二、引入Future-Listener機制來替代主動get()阻塞等待的機制。 

對於第1點,能夠回到簡介部分,該圖表清晰的描述了這個異步調用的狀態變化。當異步結果未完成時,isDone()、isSuccess()、isCancelled()均爲false,同時cause()返回null,如果完成成功,則isDone()、isSuccess()均爲true,如果完成失敗,則isDone()爲true,cause()返回not-null,如果取消完成,則

isDone()、isCancelled()均爲true。能夠看到新引入的特性能夠很清晰的表示經常使用的狀態。

對於第2點,Future-Listener機制本質上就是一種觀察者模式,Netty中的Future經過提供addListener/addListeners方法來實現對Future執行結果的監聽,一旦Future執行完成,就會觸發GenericFutureListener的operationComplete方法,在該方法中就能夠獲取Future的執行結果,這種方式比起直接get(),能有效提高Netty的吞吐量。 

 

至此,咱們就學習完了ChannelFuture,最後總結一下:

一、Netty中的全部IO操做都是異步的。這意味着任何IO調用都將當即返回,而不能保證所請求的IO操做在調用結束時完成。ChannelFuture是Channel異步IO操做的結果;

二、ChannelFuture或者說是Future,經過引入新的特性解決了原生JDK中Future對於狀態模糊不清及阻塞等待獲取結果的方式,這個新特性就是引入isSuccess()、cause()方法,同時經過Future-Listener回調機制解決不知道什麼時候能獲取到Future結果的問題。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息