Netty 是一款異步的事件驅動的網絡應用程序框架,支持快速地開發可維護的高性能的面向協議的服務器和客戶端java
早期的 Java API 只支持由本地系統套接字庫提供的所謂的阻塞函數,下面的代碼展現了一個使用傳統 Java API 的服務器代碼的普通示例編程
// 建立一個 ServerSocket 用以監聽指定端口上的鏈接請求 ServerSocket serverSocket = new ServerSocket(5000); // 對 accept 方法的調用將被阻塞,直到一個鏈接創建 Socket clientSocket = serverSocket.accept(); // 這些流對象都派生於該套接字的流對象 BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); String request, response; // 客戶端發送了 "Done" 則退出循環 while ((request = in.readLine()) != null) { if ("Done".equals(request)) { break; } // 請求被傳遞給服務器的處理方法 response = proce***equest(request); // 服務器的響應被髮送給客戶端 out.println(response); }
這段代碼只能同時處理一個鏈接,要管理多個客戶端,就要爲每一個新的客戶端 Socket 建立一個新的 Thread,讓咱們來考慮一下這種方案的影響:安全
這種併發方案對於中小數量的客戶端還算理想,但不能很好地支持更大的併發,幸運的是,還有另外一種解決方案服務器
NIO(Non-blocking I/O,也稱 New I/O),是一種同步非阻塞的 I/O 模型,也是 I/O 多路複用的基礎。傳統的 IO 流是阻塞的,這意味着,當一個線程調用讀或寫操做時,線程將被阻塞,直至數據被徹底讀取或寫入。NIO 的非阻塞模式,使一個線程進行讀或寫操做時,若是目前無數據可用時,就不作操做,而不是保持線程阻塞,因此直至數據就緒之前,線程能夠繼續作其餘事情網絡
class java.nio.channels.Selector 是 Java 非阻塞 IO 實現的關鍵。它使用事件通知 API 以肯定在一組非阻塞套接字中有哪些已經就緒並能進行 IO 相關操做。由於能夠在任什麼時候間點任意檢查讀操做或寫操做的完成狀況,因此單一線程能夠處理多個併發的鏈接併發
與阻塞 IO 模型相比,這種模型提供了更好的資源管理:框架
儘管 Java NIO 如此高效,但要作到正確和安全並不容易,在高負載下可靠和高效地處理和調度 IO 操做是一項煩瑣且容易出錯的任務,所幸,有 Netty 能幫助咱們解決問題異步
Netty 是一個普遍使用的 Java 網絡編程框架,它隱藏了 Java 高級 API 背後的複雜性,提供一個易於使用的 API 的客戶端/服務器框架。在這裏咱們將要討論 Netty 的主要構件:ide
Channel 是 Java NIO 的一個基本構造,能夠把 Channel 簡單看做是傳入(入站)或傳出(出站)數據的載體。所以,它能夠被打開或關閉,鏈接或斷開鏈接異步編程
一個回調其實就是一個方法,Netty 使用回調來處理事件,當一個回調被觸發時,相關的事件能夠被 ChannelHandler 的實現處理。下面的代碼展現了這樣一個例子:當一個新的鏈接已經創建,ChannelHandler 的 channelActive() 的回調方法將會被調用,並打印出一條信息
public class ConnectHandler extends ChannelInboundHandlerAdapter { @override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("Client " + ctx.channel().remoteAddress() + " connected"); } }
Future 提供了另外一種在操做完成時通知應用程序的方式,它將在將來的某個時刻完成,並提供對其結果的訪問。雖然 Java 提供了 Future 的一種實現,但須要手動檢查對應操做是否已經完成,或一直阻塞直到它完成,十分煩瑣。Netty 提供了本身的實現 ChannelFuture,用於執行異步操做的時候使用
ChannelFuture 提供了幾種額外的方法,這些方法使得咱們可以註冊一個或多個 ChannelFutureListener 實例。監聽器的回調方法 operationComplete() 將會在對應操做完成時被調用。每一個 Netty 的 IO 操做都會返回一個 ChannelFuture,它們都不會被阻塞,能夠同時作其餘工做,更加有效的利用資源
Channel channel = ...; // 異步地鏈接到遠程結點 ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25)); // 註冊一個 ChannelFutureListener future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { // 若是操做成功 if(future.isSuccess()) { ... } else { // 發生異常 ... } } });
Netty 使用不一樣的事件來觸發合適的動做,事件是按照與入站或出站數據流的相關性進行分類的,可能由入站數據或相關狀態更改而觸發的事件包括:
出站事件是將來將會觸發的某個動做的操做結果,包括:
每一個事件均可分發給 ChannelHandler 類中的某個用戶實現的方法,下圖展現了一個事件如何被一個 ChannelHandler 鏈處理
Netty 提供了大量預約義的 ChannelHandler 實現,供開發者使用
Netty 的異步編程模型是創建在 Future 和回調的概念之上的,將事件派發到 ChannelHandler 攔截並高速地轉換入站數據和出站數據,開發者只須要提供回調或者利用返回的 Future 便可。Netty 經過觸發事件將 Selector 從應用程序中抽象出來,消除了本需手寫的派發代碼。在內部,將會爲每一個 Channel 分配一個 EventLoop,用於處理全部事件,包括:
EventLoop 自己只有一個線程驅動,處理了一個 Channel 的全部 IO 事件,這個簡單而強大的設計消除了可能在 ChannelHandler 實現中須要進行同步的任何顧慮,所以咱們只需專一於提供正確的邏輯便可