IO


 

主題

內容

備注

概念

IO:傳統的 java.io 包,它基於流模型實現,提供了我們最熟知的一些 IO 功能,比如 File 抽象、輸入輸出流等。交互方式是同步、阻塞的方式,也就是說,在讀取輸入流或者寫入輸出流時,在讀、寫動作完成之前,線程會一直阻塞在那里,它們之間的調用是可靠的線性順序。很多時候,人們也把 java.net 下面提供的部分網絡 API,比如 Socket、ServerSocket、HttpURLConnection 也歸類到同步阻塞 IO 類庫,因為網絡通信同樣是 IO 行為。

NIO:在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以構建多路復用的、同步非阻塞 IO 程序,同時提供了更接近操作系統底層的高性能數據操作方式。

AIO:在 Java 7 中,NIO 有了進一步的改進,也就是 NIO 2,引入了異步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。異步 IO 操作基於事件和回調機制,可以簡單理解為,應用操作直接返回,而不會阻塞在那里,當后台處理完成,操作系統會通知相應線程進行后續工作。

 

緩沖區Buffer

Buffer是一個對象,它包含一些要寫入或者要讀出的數據。在NIO庫中,所有數據都是用緩沖區處理的,在讀取數據時,它是直接讀到緩沖區中的;在寫入數據時,寫入到緩沖區中。任何時候訪問NIO中的數據,都是通過緩沖區進行操作

 

通道Channel

Channel是一個通道,網絡數據通過Channel讀取和寫入。通道與流的不同之處在於通道是雙向的,流只是在一個方向上移動,而通道可以用於讀、寫或者二者同時進行。Channel可以分為兩大類:用於網絡讀寫的SelectableChannel和用於文件操作的FileChannel.

 

多路復用器Selector

多路復用器提供選擇已經就緒的任務的能力。Selector會不斷地輪詢注冊在其上的Channel,如果某個Channel上面發生讀或者寫事件,這個Channel就處於就緒狀態,會被Selector輪詢出來,然后通過SelectionKey可以獲取就緒Channel的集合,進行后續的I/O操作

 

NIO服務序列圖

 

NIO客戶端序列圖

 

NIO

代碼塊
 
 
 
 
 
public class NIOServer extends Thread {
    public void run() {
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 創建Selector和Channel
            serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
            serverSocket.configureBlocking(false);
            // 注冊到Selector,並說明關注點
            serverSocket.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                selector.select();// 阻塞等待就緒的Channel,這是關鍵點之一
                Set selectedKeys = selector.selectedKeys();
                Iterator iter = selectedKeys.iterator();
                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                   // 生產系統中一般會額外進行就緒狀態檢查
                    sayHelloWorld((ServerSocketChannel) key.channel());
                    iter.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void sayHelloWorld(ServerSocketChannel server) throws IOException {
        try (SocketChannel client = server.accept();) {          client.write(Charset.defaultCharset().encode("Hello world!"));
        }
    }
   // 省略了與前面類似的main
}
 
  • 首先,通過Selector.open()創建一個Selector,作為類似調度員的角色。

  • 然后,創建一個ServerSocketChannel,並且向Selector注冊,通過指定SelectionKey.OP_ACCEPT,告訴調度員,它關注的是新的連接請求。

    注意,為什么我們要明確配置非阻塞模式呢?這是因為阻塞模式下,注冊操作是不允許的,會拋出IllegalBlockingModeException異常。

  • Selector阻塞在select操作,當有Channel發生接入請求,就會被喚醒。

  • 在sayHelloWorld方法中,通過SocketChannel和Buffer進行數據操作

 

多路復用

   

Netty與NIO

Netty在基礎的NIO等類庫之上進行了很多改進,例如:

  • 更加優雅的Reactor模式實現、靈活的線程模型、利用EventLoop等創新性的機制,可以非常高效地管理成百上千的Channel。

  • 充分利用了Java的Zero-Copy機制,並且從多種角度,“斤斤計較”般的降低內存分配和回收的開銷。例如,使用池化的Direct Buffer等技術,在提高IO性能的同時,減少了對象的創建和銷毀;利用反射等技術直接操縱SelectionKey,使用數組而不是Java容器等。

  • 使用更多本地代碼。例如,直接利用JNI調用Open SSL等方式,獲得比Java內建SSL引擎更好的性能。

  • 在通信協議、序列化等其他角度的優化。

Netty是一個異步的、基於事件Client/Server的網絡框架,目標是提供一種簡單、快速構建網絡應用的方式,同時保證高吞吐量、低延時、高可靠性。

 

Reactor模式

   

Netty線程模型

   

Pipelining、EventLoop等部分的設計實現細節

   

Netty的內存管理機制

   

NIO早期Epoll空轉問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM