W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
在前面的章節(jié)中我們已經(jīng)對(duì)非阻塞I/O進(jìn)行解釋了,它不會(huì)強(qiáng)制要求我們等待操作完成。那么基于這個(gè)條件,真正的異步I/O的作用更進(jìn)一步:一個(gè)異步方法完成時(shí)立即返回并直接或稍后通知用戶。所以,在一個(gè)網(wǎng)絡(luò)環(huán)境的異步模型中可以更加有效地利用資源,可以快速連續(xù)執(zhí)行多個(gè)調(diào)用。除了Netty 的異步模型,本章還將介紹回調(diào),future 及其組合使用。
Channel 是 NIO 基本的結(jié)構(gòu)。它代表了一個(gè)用于連接到實(shí)體如硬件設(shè)備、文件、網(wǎng)絡(luò)套接字或程序組件,能夠執(zhí)行一個(gè)或多個(gè)不同的 I/O 操作(例如讀或?qū)懀┑拈_放連接。
現(xiàn)在,把 Channel 想象成一個(gè)可以“打開”或“關(guān)閉”,“連接”或“斷開”和作為傳入和傳出數(shù)據(jù)的運(yùn)輸工具。
callback (回調(diào))是一個(gè)簡(jiǎn)單的方法,提供給另一種方法作為引用,這樣后者就可以在某個(gè)合適的時(shí)間調(diào)用前者。這種技術(shù)被廣泛使用在各種編程的情況下,最常見的方法之一通知給其他人操作已完成。
Netty 內(nèi)部使用回調(diào)處理事件時(shí)。一旦這樣的回調(diào)被觸發(fā),事件可以由接口 ChannelHandler 的實(shí)現(xiàn)來(lái)處理。如下面的代碼,一旦一個(gè)新的連接建立了,調(diào)用 channelActive(),并將打印一條消息。
Listing 1.2 ChannelHandler triggered by a callback
public class ConnectHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { //1
System.out.println(
"Client " + ctx.channel().remoteAddress() + " connected");
}
}
1.當(dāng)建立一個(gè)新的連接時(shí)調(diào)用 ChannelActive()
Future 提供了另外一種通知應(yīng)用操作已經(jīng)完成的方式。這個(gè)對(duì)象作為一個(gè)異步操作結(jié)果的占位符,它將在將來(lái)的某個(gè)時(shí)候完成并提供結(jié)果。
JDK 附帶接口 java.util.concurrent.Future ,但所提供的實(shí)現(xiàn)只允許您手動(dòng)檢查操作是否完成或阻塞了。這是很麻煩的,所以 Netty 提供自己了的實(shí)現(xiàn),ChannelFuture,用于在執(zhí)行異步操作時(shí)使用。
ChannelFuture 提供多個(gè)附件方法來(lái)允許一個(gè)或者多個(gè) ChannelFutureListener 實(shí)例。這個(gè)回調(diào)方法 operationComplete() 會(huì)在操作完成時(shí)調(diào)用。事件監(jiān)聽者能夠確認(rèn)這個(gè)操作是否成功或者是錯(cuò)誤。如果是后者,我們可以檢索到產(chǎn)生的 Throwable。簡(jiǎn)而言之, ChannelFutureListener 提供的通知機(jī)制不需要手動(dòng)檢查操作是否完成的。
每個(gè) Netty 的 outbound I/O 操作都會(huì)返回一個(gè) ChannelFuture;這樣就不會(huì)阻塞。這就是 Netty 所謂的“自底向上的異步和事件驅(qū)動(dòng)”。
下面例子簡(jiǎn)單的演示了作為 I/O 操作的一部分 ChannelFuture 的返回。當(dāng)調(diào)用 connect() 將會(huì)直接是非阻塞的,并且調(diào)用在背后完成。由于線程是非阻塞的,所以無(wú)需等待操作完成,而可以去干其他事,因此這令資源利用更高效。
Listing 1.3 Callback in action
Channel channel = ...;
//不會(huì)阻塞
ChannelFuture future = channel.connect(
new InetSocketAddress("192.168.0.1", 25));
1.異步連接到遠(yuǎn)程地址
下面代碼描述了如何利用 ChannelFutureListener 。首先,連接到遠(yuǎn)程地址。接著,通過(guò) ChannelFuture 調(diào)用 connect() 來(lái) 注冊(cè)一個(gè)新ChannelFutureListener。當(dāng)監(jiān)聽器被通知連接完成,我們檢查狀態(tài)。如果是成功,就寫數(shù)據(jù)到 Channel,否則我們檢索 ChannelFuture 中的Throwable。
注意,錯(cuò)誤的處理取決于你的項(xiàng)目。當(dāng)然,特定的錯(cuò)誤是需要加以約束 的。例如,在連接失敗的情況下你可以嘗試連接到另一個(gè)。
Listing 1.4 Callback in action
Channel channel = ...;
//不會(huì)阻塞
ChannelFuture future = channel.connect( //1
new InetSocketAddress("192.168.0.1", 25));
future.addListener(new ChannelFutureListener() { //2
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) { //3
ByteBuf buffer = Unpooled.copiedBuffer(
"Hello", Charset.defaultCharset()); //4
ChannelFuture wf = future.channel().writeAndFlush(buffer); //5
// ...
} else {
Throwable cause = future.cause(); //6
cause.printStackTrace();
}
}
});
1.異步連接到遠(yuǎn)程對(duì)等節(jié)點(diǎn)。調(diào)用立即返回并提供 ChannelFuture。
2.操作完成后通知注冊(cè)一個(gè) ChannelFutureListener 。
3.當(dāng) operationComplete() 調(diào)用時(shí)檢查操作的狀態(tài)。
4.如果成功就創(chuàng)建一個(gè) ByteBuf 來(lái)保存數(shù)據(jù)。
5.異步發(fā)送數(shù)據(jù)到遠(yuǎn)程。再次返回ChannelFuture。
6.如果有一個(gè)錯(cuò)誤則拋出 Throwable,描述錯(cuò)誤原因。
Netty 使用不同的事件來(lái)通知我們更改的狀態(tài)或操作的狀態(tài)。這使我們能夠根據(jù)發(fā)生的事件觸發(fā)適當(dāng)?shù)男袨椤?/p>
這些行為可能包括:
由于 Netty 是一個(gè)網(wǎng)絡(luò)框架,事件很清晰的跟入站或出站數(shù)據(jù)流相關(guān)。因?yàn)橐恍┦录赡苡|發(fā)傳入的數(shù)據(jù)或狀態(tài)的變化包括:
出站事件是由于在未來(lái)操作將觸發(fā)一個(gè)動(dòng)作。這些包括:
每個(gè)事件都可以分配給用戶實(shí)現(xiàn)處理程序類的方法。這說(shuō)明了事件驅(qū)動(dòng)的范例可直接轉(zhuǎn)換為應(yīng)用程序構(gòu)建塊。
圖1.3顯示了一個(gè)事件可以由一連串的事件處理器來(lái)處理
Figure 1.3 Event Flow
Netty 的 ChannelHandler 是各種處理程序的基本抽象。想象下,每個(gè)處理器實(shí)例就是一個(gè)回調(diào),用于執(zhí)行對(duì)各種事件的響應(yīng)。
在此基礎(chǔ)之上,Netty 也提供了一組豐富的預(yù)定義的處理程序,您可以開箱即用。比如,各種協(xié)議的編解碼器包括 HTTP 和 SSL/TLS。在內(nèi)部,ChannelHandler 使用事件和 future 本身,創(chuàng)建具有 Netty 特性抽象的消費(fèi)者。
Netty 的異步編程模型是建立在 future 和 callback 的概念上的。所有這些元素的協(xié)同為自己的設(shè)計(jì)提供了強(qiáng)大的力量。
攔截操作和轉(zhuǎn)換入站或出站數(shù)據(jù)只需要您提供回調(diào)或利用 future 操作返回的。這使得鏈操作簡(jiǎn)單、高效,促進(jìn)編寫可重用的、通用的代碼。一個(gè) Netty 的設(shè)計(jì)的主要目標(biāo)是促進(jìn)“關(guān)注點(diǎn)分離”:你的業(yè)務(wù)邏輯從網(wǎng)絡(luò)基礎(chǔ)設(shè)施應(yīng)用程序中分離。
Netty 通過(guò)觸發(fā)事件從應(yīng)用程序中抽象出 Selector,從而避免手寫調(diào)度代碼。EventLoop 分配給每個(gè) Channel 來(lái)處理所有的事件,包括
該 EventLoop 本身是由一個(gè)線程驅(qū)動(dòng),它給一個(gè) Channel 處理所有的 I/O 事件,并且在 EventLoop 的生命周期內(nèi)不會(huì)改變。這個(gè)簡(jiǎn)單而強(qiáng)大的線程模型消除你可能對(duì)你的 ChannelHandler 同步的任何關(guān)注,這樣你就可以專注于提供正確的回調(diào)邏輯來(lái)執(zhí)行。該 API 是簡(jiǎn)單和緊湊。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: