Netty由哪幾個(gè)部分構(gòu)成?

2020-10-16 17:04 更新

在前面的章節(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

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))

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

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ò)誤原因。

Event 和 Handler

Netty 使用不同的事件來(lái)通知我們更改的狀態(tài)或操作的狀態(tài)。這使我們能夠根據(jù)發(fā)生的事件觸發(fā)適當(dāng)?shù)男袨椤?/p>

這些行為可能包括:

  • 日志
  • 數(shù)據(jù)轉(zhuǎn)換
  • 流控制
  • 應(yīng)用程序邏輯

由于 Netty 是一個(gè)網(wǎng)絡(luò)框架,事件很清晰的跟入站或出站數(shù)據(jù)流相關(guān)。因?yàn)橐恍┦录赡苡|發(fā)傳入的數(shù)據(jù)或狀態(tài)的變化包括:

  • 活動(dòng)或非活動(dòng)連接
  • 數(shù)據(jù)的讀取
  • 用戶事件
  • 錯(cuò)誤

出站事件是由于在未來(lái)操作將觸發(fā)一個(gè)動(dòng)作。這些包括:

  • 打開或關(guān)閉一個(gè)連接到遠(yuǎn)程
  • 寫或沖刷數(shù)據(jù)到 socket

每個(gè)事件都可以分配給用戶實(shí)現(xiàn)處理程序類的方法。這說(shuō)明了事件驅(qū)動(dòng)的范例可直接轉(zhuǎn)換為應(yīng)用程序構(gòu)建塊。

圖1.3顯示了一個(gè)事件可以由一連串的事件處理器來(lái)處理

Figure 1.3 Event Flow

Figure%201

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)者。

整合

FUTURE, CALLBACK 和 HANDLER

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)用程序中分離。

SELECTOR, EVENT 和 EVENT LOOP

Netty 通過(guò)觸發(fā)事件從應(yīng)用程序中抽象出 Selector,從而避免手寫調(diào)度代碼。EventLoop 分配給每個(gè) Channel 來(lái)處理所有的事件,包括

  • 注冊(cè)感興趣的事件
  • 調(diào)度事件到 ChannelHandler
  • 安排進(jìn)一步行動(dòng)

該 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)單和緊湊。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)