該文章基于個(gè)人的理解,翻譯自netty5.0 API。
綜述
ChannelFuture的作用是用來(lái)保存Channel異步操作的結(jié)果。
我們知道,在Netty中所有的I/O操作都是異步的。這意味著任何的I/O調(diào)用都將立即返回,而不保證這些被請(qǐng)求的I/O操作在調(diào)用結(jié)束的時(shí)候已經(jīng)完成。取而代之地,你會(huì)得到一個(gè)返回的ChannelFuture實(shí)例,這個(gè)實(shí)例將給你一些關(guān)于I/O操作結(jié)果或者狀態(tài)的信息。
對(duì)于一個(gè)ChannelFuture可能已經(jīng)完成,也可能未完成。當(dāng)一個(gè)I/O操作開(kāi)始的時(shí)候,一個(gè)新的future對(duì)象就會(huì)被創(chuàng)建。在開(kāi)始的時(shí)候,新的future是未完成的狀態(tài)--它既非成功、失敗,也非被取消,因?yàn)镮/O操作還沒(méi)有結(jié)束。如果I/O操作以成功、失敗或者被取消中的任何一種狀態(tài)結(jié)束了,那么這個(gè)future將會(huì)被標(biāo)記為已完成,并包含更多詳細(xì)的信息(例如:失敗的原因)。請(qǐng)注意,即使是失敗和被取消的狀態(tài),也是屬于已完成的狀態(tài)。
下面這張圖來(lái)自于官方文檔,用于說(shuō)明各種狀態(tài)的關(guān)系:

各種各樣的方法被提供,用來(lái)檢查I/O操作是否已完成、等待完成,并尋回I/O操作的結(jié)果。它同樣允許你添加ChannelFutureListener,以便于在I/O操作完成的時(shí)候,你能夠獲得通知。
優(yōu)先使用addListener(GenericFutureListener),而非await()
當(dāng)做了一個(gè)I/O操作并有任何后續(xù)任務(wù)的時(shí)候,推薦優(yōu)先使用addListener(GenericFutureListener)的方式來(lái)獲得通知,而非await()。
addListener(GenericFutureListener)是非阻塞的。它會(huì)把特定的ChannelFutureListener添加到ChannelFuture中,然后I/O線(xiàn)程會(huì)在I/O操作相關(guān)的future完成的時(shí)候通知監(jiān)聽(tīng)器。ChannelFutureListener會(huì)利于最佳的性能和資源的利用,因?yàn)樗稽c(diǎn)阻塞都沒(méi)有。然而,如果你不使用基于事件驅(qū)動(dòng)的編程方式,去實(shí)現(xiàn)一個(gè)后續(xù)式的邏輯會(huì)變得詭異和難于理解。
對(duì)比來(lái)看,await()是一個(gè)阻塞的操作。一旦被調(diào)用,調(diào)用者線(xiàn)程會(huì)阻塞,直到操作完成。使用await()來(lái)實(shí)現(xiàn)一個(gè)后續(xù)式的邏輯會(huì)更容易,但是調(diào)用者線(xiàn)程會(huì)非常沒(méi)必要的阻塞直到I/O操作完成,并且內(nèi)部的線(xiàn)程通知是相對(duì)來(lái)說(shuō)代價(jià)昂貴的。更有甚者,在一些特定的情況下會(huì)產(chǎn)生死鎖,下面是對(duì)這種情況的描述:
// BAD - NEVER DO THIS
@Override
public void channelRead(ChannelHandlerContext ctx, GoodByeMessage msg) {
ChannelFuture future = ctx.channel().close();
future.awaitUninterruptibly();
// Perform post-closure operation
// ...
}
// GOOD
@Override
public void channelRead(ChannelHandlerContext ctx, GoodByeMessage msg) {
ChannelFuture future = ctx.channel().close();
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
// Perform post-closure operation
// ...
}
});
}
撇開(kāi)上面提到的缺點(diǎn)不談,確實(shí)還是有一些情況在調(diào)用await()的時(shí)候會(huì)更方便的。在這種情況下,請(qǐng)確保你不是在一個(gè)I/O線(xiàn)程中調(diào)用的await()。否則,為了避免死鎖的情況,BlockingOperationException將被提出。
不要混淆I/O timeout和await timeout
你在使用Future.await(long), Future.await(long, TimeUnit),F(xiàn)uture.awaitUninterruptibly(long),或者Future.awaitUninterruptibly(long, TimeUnit)的時(shí)候,指定的timeout的值和I/O timeout一點(diǎn)關(guān)系都沒(méi)有。如果一個(gè)操作超時(shí)了,future將會(huì)被標(biāo)記為已完成-失敗,就像上面的圖中描述的那樣。例如,連接超時(shí)應(yīng)當(dāng)通過(guò)一個(gè)傳輸特定的選項(xiàng)來(lái)配置:
// 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
}