天天看點

《Netty 權威指南》—— NIO類庫簡介

在介紹NIO程式設計之前,我們首先需要澄清一個概念,NIO到底是什麼的簡稱?有人稱之為New IO,因為它相對于之前的IO類庫是新增的,是以被稱為New IO,這是它的官方叫法。但是,由于之前老的IO類庫是阻塞IO,New IO類庫的目标就是要讓JAVA支援非阻塞IO,是以,更多的人喜歡稱之為非阻塞IO(Non-block IO),由于非阻塞IO更能夠展現NIO的特點,是以本書使用的NIO都指的是非阻塞IO。

與Socket類和ServerSocket類相對應,NIO也提供了SocketChannel和ServerSocketChannel兩種不同的套接字通道實作。這兩種新增的通道都支援阻塞和非阻塞兩種模式。阻塞模式使用非常簡單,但是性能和可靠性都不好,非阻塞模式正好相反。開發人員一般可以根據自己的需要來選擇合适的模式,一般來說,低負載、低并發的應用程式可以選擇同步阻塞IO以降低程式設計複雜度。但是對于高負載、高并發的網絡應用,需要使用NIO的非阻塞模式進行開發。

下面的小節首先介紹NIO程式設計中的一些基本概念,然後通過NIO服務端的序列圖和源碼講解,讓大家快速的熟悉NIO程式設計的關鍵步驟和API的使用。如果你已經熟悉了NIO程式設計,可以跳過2.3章節繼續學習後面的章節。

2.3.1. NIO類庫簡介

新的輸入/輸出 (NIO) 庫是在 JDK 1.4 中引入的。NIO 彌補了原來同步阻塞I/O 的不足,它在标準 Java 代碼中提供了高速的、面向塊的 I/O。通過定義包含資料的類,以及通過以塊的形式處理這些資料,NIO 不用使用本機代碼就可以利用底層優化,這是原來的 I/O 包所無法做到的。下面我們對NIO的一些概念和功能做下簡單介紹,以便大家能夠快速的了解NIO類庫和相關概念。

2.3.1.1. 緩沖區Buffer

我們首先介紹緩沖區(Buffer)的概念,Buffer 是一個對象, 它包含一些要寫入或者要讀出的資料。 在 NIO類庫 中加入 Buffer 對象,展現了新庫與原 I/O 的一個重要差別。在面向流的 I/O 中,我們将資料直接寫入或者将資料直接讀到 Stream 對象中。

在 NIO 庫中,所有資料都是用緩沖區進行處理的。在讀取資料時,它是直接讀到緩沖區中;在寫入資料時,它也是寫入到緩沖區中。任何時候通路 NIO 中的資料,我們都是通過緩沖區進行讀寫操作。

緩沖區實質上是一個數組。通常它是一個位元組數組(ByteBuffer),也可以使用其它種類的數組。但是一個緩沖區不僅僅是一個數組,緩沖區提供了對資料的結構化通路,及維護讀寫位置(limit)等資訊。

最常用的緩沖區是ByteBuffer,一個ByteBuffer提供了一組功能用于操作byte數組。除了ByteBuffer,還有其它的一些緩沖區,事實上,每一種Java基本類型(除了Boolean類型)都對應有一種緩沖區,如下所示:

ByteBuffer:位元組緩沖區

CharBuffer:字元緩沖區

ShortBuffer:短整型緩沖區

IntBuffer:整形緩沖區

LongBuffer:長整形緩沖區

FloatBuffer:浮點型緩沖區

DoubleBuffer:雙精度浮點型緩沖區

緩沖區的類圖繼承關系如下所示:

《Netty 權威指南》—— NIO類庫簡介

Buffer繼承關系圖

每一個Buffer類都是Buffer接口的一個子執行個體。除了 ByteBuffer,每一個 Buffer 類都有完全一樣的操作,隻是它們所處理的資料類型不一樣。因為大多數标準I/O操作都使用ByteBuffer,是以它除了具有一般緩沖區的操作之外還提供一些特有的操作,友善網絡讀寫。

Channel是一個通道,可以通過它讀取和寫入資料,它就像自來水管一樣,網絡資料通過Channel讀取和寫入。通道與流的不同之處在于通道是雙向的。而流隻是在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類),而通道可以用于讀、寫或者同時用于讀寫。

因為Channel是全雙工的,是以它可以比流更好地映射底層作業系統的API。特别是在UNIX網絡程式設計模型中,底層作業系統的通道都是全雙工的,同時支援讀寫操作。

Channel的類圖繼承關系如下:

《Netty 權威指南》—— NIO類庫簡介

Channel繼承關系類圖

自頂向下,前三層主要是Channel接口,用于定義它的功能,後面是一些具體的功能類(抽象類),從類圖可以看出,實際上Channel可以分為兩大類,分别是用于網絡讀寫的SelectableChannel和用于檔案操作的FileChannel。

本書涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子類,關于它們的具體用法将在後續的代碼中展現。

在本節中,我們将探索多路複用器Selector,它是JAVA NIO程式設計的基礎,熟練的掌握Selector對于掌握NIO程式設計至關重要。多路複用器提供選擇已經就緒的任務的能力。簡單來講,Selector會不斷的輪詢注冊在其上的Channel,如果某個Channel上面有新的TCP連接配接接入、讀和寫事件,這個Channel就處于就緒狀态,會被Selector輪詢出來,然後通過SelectionKey可以擷取就緒Channel的集合進行後續的IO操作。

一個多路複用器Selector可以同時輪詢多個Channel,由于JDK使用了epoll()代替傳統的select實作,是以它并沒有最大連接配接句柄1024/2048的限制。這也就意味着隻需要一個線程負責Selector的輪詢,就可以接入成千上萬的用戶端,這的确是一個巨大的改進。