天天看點

《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的輪詢,就可以接入成千上萬的用戶端,這的确是一個巨大的改進。