Netty 異步和數(shù)據(jù)驅動

2021-04-22 14:57 更新

在學習Netty的異步和數(shù)據(jù)驅動之前,我們先來簡單的了解一下Netty

Netty是什么

Netty 是一個利用 Java 的高級網(wǎng)絡的能力,隱藏了Java背后的復雜性然后提供了一個易于使用的 API 的客戶端/服務器框架。Netty 的高性能和可擴展性,可以作為你自己的獨特的應用,讓你更用心的花時間在你有興趣的東西上。

Netty處理一些高并發(fā)的網(wǎng)絡問題的時候會體現(xiàn)出怎樣的價值呢?想知道的話,答案就在本章中。本章除了解釋上述問題,還將為大家講解Netty的基本概念以及Netty的工具包的構成成分。

Netty的歷史

在網(wǎng)絡發(fā)展初期,需要花很多時間來學習 socket 的復雜,尋址等等,在 C socket 庫上進行編碼,并需要在不同的操作系統(tǒng)上做不同的處理。

Java 早期版本(1995-2002)介紹了足夠的面向對象的糖衣來隱藏一些復雜性,但實現(xiàn)復雜的客戶端-服務器協(xié)議仍然需要大量的樣板代碼(和進行大量的監(jiān)視才能確保他們是對的)。

這些早期的 Java API(java.net)只能通過原生的 socket 庫來支持所謂的“blocking(阻塞)”的功能。一個簡單的例子

Listing 1.1 Blocking I/O Example

ServerSocket serverSocket = new ServerSocket(portNumber);//1
Socket clientSocket = serverSocket.accept();             //2
BufferedReader in = new BufferedReader(                     //3
        new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out =
        new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
while ((request = in.readLine()) != null) {                 //4
    if ("Done".equals(request)) {                         //5
        break;
    }
}
response = processRequest(request);                        //6
out.println(response);                                    //7
}                                                        //8

1.ServerSocket 創(chuàng)建并監(jiān)聽端口的連接請求

2.accept() 調(diào)用阻塞,直到一個連接被建立了。返回一個新的 Socket 用來處理 客戶端和服務端的交互

3.流被創(chuàng)建用于處理 socket 的輸入和輸出數(shù)據(jù)。BufferedReader 讀取從字符輸入流里面的文本。PrintWriter 打印格式化展示的對象讀到本文輸出流

4.處理循環(huán)開始 readLine() 阻塞,讀取字符串直到最后是換行或者輸入終止。

5.如果客戶端發(fā)送的是“Done”處理循環(huán)退出

6.執(zhí)行方法處理請求,返回服務器的響應

7.響應發(fā)回客戶端

8.處理循環(huán)繼續(xù)

顯然,這段代碼限制每次只能處理一個連接。為了實現(xiàn)多個并行的客戶端我們需要分配一個新的 Thread 給每個新的客戶端 Socket(當然需要更多的代碼)。但考慮使用這種方法來支持大量的同步,長連接。在任何時間點多線程可能處于休眠狀態(tài),等待輸入或輸出數(shù)據(jù)。這很容易使得資源的大量浪費,對性能產(chǎn)生負面影響。當然,有一種替代方案。

除了示例中所示阻塞調(diào)用,原生 socket 庫同時也包含了非阻塞 I/O 的功能。這使我們能夠確定任何一個 socket 中是否有數(shù)據(jù)準備讀或寫。我們還可以設置標志,因為讀/寫調(diào)用如果沒有數(shù)據(jù)立即返回;就是說,如果一個阻塞被調(diào)用后就會一直阻塞,直到處理完成。通過這種方法,會帶來更大的代碼的復雜性成本,其實我們可以獲得更多的控制權來如何利用網(wǎng)絡資源。

JAVA NIO

在 2002 年,Java 1.4 引入了非阻塞 API 在 java.nio 包(NIO)。

"New"還是"Nonblocking"?

NIO 最初是為 New Input/Output 的縮寫。然而,Java 的 API 已經(jīng)存在足夠長的時間,它不再是新的?,F(xiàn)在普遍使用的縮寫來表示Nonblocking I/O (非阻塞 I/O)。另一方面,一般(包括作者)指阻塞 I/O 為 OIO 或 Old Input/Output。你也可能會遇到普通 I/O。

我們已經(jīng)展示了在 Java 的 I/O 阻塞一例例子。圖 1.1 展示了方法 必須擴大到處理多個連接:給每個連接創(chuàng)建一個線程,有些連接是空閑的!顯然,這種方法的可擴展性將是受限于可以在 JVM 中創(chuàng)建的線程數(shù)。

Figure 1.1 Blocking I/O

Figure%201

當你的應用中連接數(shù)比較少,這個方案還是可以接受。當并發(fā)連接超過10000 時,context-switching(上下文切換)開銷將是明顯的。此外,每個線程都有一個默認的堆棧內(nèi)存分配了 128K 和 1M 之間的空間。考慮到整體的內(nèi)存和操作系統(tǒng)需要處理 100000 個或更多的并發(fā)連接資源,這似乎是一個不理想的解決方案。

SELECTOR

相比之下,圖1.2 顯示了使用非阻塞I/O,主要是消除了這些方法 約束。在這里,我們介紹了“Selector”,這是 Java 的無阻塞 I/O 實現(xiàn)的關鍵。

Figure 1.2 Nonblocking I/O

v2-d2cbae084dd6d8babb9fb9da7351334f_720w

Selector 最終決定哪一組注冊的 socket 準備執(zhí)行 I/O。正如我們之前所解釋的那樣,這 I/O 操作設置為非阻塞模式。通過通知,一個線程可以同時處理多個并發(fā)連接。(一個 Selector 由一個線程通常處理,但具體實施可以使用多個線程。)因此,每次讀或寫操作執(zhí)行能立即檢查完成??傮w而言,該模型提供了比 阻塞 I/O 模型 更好的資源使用,因為

  • 可以用較少的線程處理更多連接,這意味著更少的開銷在內(nèi)存和上下文切換上
  • 當沒有 I/O 處理時,線程可以被重定向到其他任務上。

如果你要直接使用這些 Java API 構建的 NIO 建立你的應用程序,只是這樣做正確和安全是無法保證的。實現(xiàn)可靠和可擴展的 event-processing(事件處理器)來處理和調(diào)度數(shù)據(jù)并保證盡可能有效地,這是一個繁瑣和容易出錯的任務,最好留給專家 - Netty。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號