《Netty權威指南》(一)走進 Java NIO

 

1.1 I/O 基礎入門

Java1.4 以前的早期版本,對 I/O 的支持存在以下問題:linux

  • 沒有數據緩衝區,I/O 性能存在問題;
  • 沒有 C 或者 C++ 中的 Channel 概念,只有輸入和輸出流;
  • 同步阻塞式 I/O 通訊(BIO),一般會致使通訊線程被長時間阻塞;
  • 支持的字符集有限,硬件可移植性很差。

 

1.1.1 Linux 網絡 I/O 模型

根據 UNIX 網絡編程對 I/O 模型的分類,UNIX 提供了 5 種 I/O 模型:正則表達式

  1. 阻塞 I/O(blocking I/O)
  2. 非阻塞 I/O(nonblocking I/O)
  3. I/O 複用(I/O multiplexing (select and poll))
  4. 信號驅動 I/O(signal driven I/O (SIGIO))
  5. 異步 I/O(asynchronous I/O (the POSIX aio_ functions))

對於一個套接口上的輸入操做,一般包括2個階段:編程

  1. 等待數據準備就緒。等待數據從網絡中到達,當包到達時,它被複制到內核緩衝區。
  2. 數據從內核複製到用戶空間。將數據從內核緩衝區複製到應用進程緩衝區。

(1)阻塞 I/O 模型

最經常使用的 I/O 模型就是阻塞 I/O 模型,缺省情形下,全部文件操做都是阻塞的。咱們以套接字接口爲例來說解此模型:在進程空間中調用 recvfrom,其系統調用直到數據包到達且被複制到應用進程的緩衝區中或者發生錯誤時才返回,在此期間一直會等待,進程在從調用 recvfrom 開始到它返回的整段時間內都是被阻塞的,所以被稱爲阻塞 I/O 模型。服務器

(2)非阻塞 I/O 模型

recvfrom 從應用層到內核的時候,若是該緩衝區沒有數據的話,就直接返回一個 EWOULDBLOCK 錯誤,通常都對非阻塞 I/O 模型進行輪詢檢查這個狀態,看內核是否是有數據到來。網絡

(3)I/O 多路複用模型

Linux 提供 select/poll,進程經過將一個或多個 fd 傳遞給 select 或 poll 系統調用,阻塞在 select 操做上,這樣 select/poll 能夠幫咱們偵測多個 fd 是否處於就緒狀態。select/poll 是順序掃描 fd 是否就緒,並且支持的 fd 數量有限,所以它的使用收到了一些制約。Linux 還提供了一個 epoll 系統調用,epoll 使用基於事件驅動方式代替順序掃描,所以性能更高。當有 fd 就緒時,當即回調函數 rollback。多線程

(4)信號驅動 I/O 模型

首先開啓套接口信號驅動 I/O 功能,並經過系統調用 sigaction 執行一個信號處理函數(此係統調用當即返回,進程繼續工做,它是非阻塞的)。當數據準備就緒時,就爲該進程生成一個 SIGIO 信號,經過信號回調通知應用程序調用 recvfrom 來讀取數據,並通知主循環函數處理數據。併發

(5)異步 I/O 模型

告知內核啓動某個操做,並讓內核在整個操做完成後(包括將數據從內核複製到用戶本身的緩衝區)通知咱們。這種模型與信號驅動模型的主要區別是:信號驅動 I/O 由內核通知咱們什麼時候能夠開始一個 I/O 操做;異步 I/O 模型由內核通知咱們 I/O 操做什麼時候已經完成。異步

5種 I/O 模型比較
socket

 

1.1.2 I/O 多路複用技術

在 I/O 編程過程當中,當須要處理多個客戶端接入請求時,能夠利用多線程或者 I/O 多路複用技術進行處理。I/O 多路複用技術經過把多個 I/O 的阻塞複用到同一個 select 的阻塞上,從而使得系統在單線程的狀況下能夠同時處理多個客戶端請求。與傳統的多線程/多進程模型比,I/O 多路複用的最大優點是系統開銷小,系統不須要建立新的額外進程或者線程,也不須要維護這些進程和線程的運行,下降了系統的維護工做量,節省了系統資源。

I/O 多路複用的主要應用場景以下:

  • 服務器須要同時處理多個處於監聽狀態或者多個鏈接狀態的套接字;
  • 服務器須要同時處理多種網絡協議的套接字。

目前支持 I/O 多路服用的系統調用有 select、pselect、poll、epoll,在 Linux 網絡編程過程當中,很長一段時間都使用 select 作輪詢和網絡事件通知。爲了克服 select 的缺點,epoll 做了很大的改進:

  1. 支持一個進程打開的 socket 描述符(FD)不受限制(僅受限於操做系統的最大文件句柄數)。
  2. I/O 效率不會隨着 FD 數據的增長而線性降低。
  3. 使用 mmap 加速內核與用戶空間的消息傳遞。
  4. epoll 的 API 更加簡單。包括建立一個 epoll 描述符、添加監聽時間、阻塞等待所監聽的事件發生、關閉 epoll 描述符等。

注:epoll 是 Linux 內核爲處理大批量文件描述符而做了改進的 poll,是 Linux 下多路複用 IO接口 select/poll 的加強版本,它能顯著提升程序在大量併發鏈接中只有少許活躍的狀況下的系統 CPU 利用率。

 

2. Java 的 I/O 演進

從JDK1.0 到 JDK1.3,Java 的 I/O 類庫都很是原始,不少 UNIX 網絡編程中的概念或者接口在 I/O 類庫中都沒有體現,例如 Pipe、Channel、Buffer 和 Selector 等。

2002年發佈的 JDK1.4 時,NIO 以 JSR-51 的身份正式隨 JDK 發佈。它新增了個 java.nio 包,提供了不少進行異步 I/O 開發的 API 和類庫,主要的類和接口以下:

  • 進行異步 I/O 操做的緩衝區 ByteBuffer 等;
  • 進行異步 I/O 操做的管道 Pipe;
  • 進行各類 I/O 操做的 Channel,包括 ServerSocketChannel 和 SocketChannel;
  • 多種字符集的編解碼能力;
  • 實現非阻塞 I/O 操做的多路複用器 selector;
  • 基於流行的 Perl 實現的正則表達式類庫;
  • 文件通道 FileChannel。

2011年7月28日,JDK1.7 正式發佈,將原來的 NIO 類庫進行了升級,被稱爲 NIO2.0。主要包括以下三個方面的改進:

  • 提供可以批量獲取文件屬性的 API,這些 API 具備平臺無關性。提供了標準文件系統的 SPI,供各個服務提供商擴展實現;
  • 提供 AIO 功能,支持基於我呢見的異步 I/O 操做和針對網絡套接字的異步操做;
  • 完成 JSR-51 定義的通道功能,包括對配置和多播數據報的支持等。
相關文章
相關標籤/搜索