NIO的特性/NIO與IO區別:java
讀數據和寫數據方式:數組
NIO三大核心組件:Channels 、Buffers 、Selectors
Netty對應也有幾大組件:網絡
Channel: 它表明一個到實體(如一個硬件設備、一個文件、一個網絡套接字或者一個可以執行一個或者多個不一樣的I/O操做的程序組件)的開放鏈接,如讀操做和寫操做;將會爲每一個channel分配一個EventLoop
EventLoop: 控制流、多線程處理、併發
EventLoopGroup
ChannelHandler: 爲了響應特定事件而被執行的回調: 每個handler都在一個HanderPipeline中
ChannelFuture: 異步通知多線程
其實核心是最後的 ByteBuffer,前面的一大串類只是包裝了一下它而已,咱們使用最多的一般也是 ByteBuffer。
MappedByteBuffer 用於實現直接內存映射mmp。併發
Buffer 和數組差很少,它有 position、limit、capacity 幾個重要屬性。put() 一下數據、flip() 切換到讀模式、而後用 get() 獲取數據、clear() 一下清空數據、從新回到 put() 寫入數據。app
NIO的數據傳輸是基於緩衝區的,ByteBuffer正是NIO數據傳輸中所使用的緩衝區抽象。ByteBuffer支持在 堆外分配內存DirectBuffer,而且嘗試避免在執行I/O操做中的多餘複製。經過JNI調用來在堆外分配內存(調用malloc()函數在JVM堆外分配內存),這主要是爲了不額外的緩衝區複製操做。
通常的I/O操做都須要進行系統調用,這樣會先切換到內核態,內核態要先從文件讀取數據到它的緩衝區,只有等數據準備完畢後,纔會從內核態把數據寫到用戶態,所謂的阻塞IO其實就是說的在等待數據準備好的這段時間內進行阻塞。若是想要避免這個額外的內核操做,能夠經過使用mmap(虛擬內存映射)的方式來讓用戶態直接操做文件。異步
網絡傳輸的基本單位是字節,在Java NIO中提供了ByteBuffer做爲字節緩衝區容器,但該類的API使用起來不太方便,因此Netty實現了ByteBuf做爲其替代品,下面是使用ByteBuf的優勢:socket
相比ByteBuffer使用起來更加簡單。
經過內置的複合緩衝區類型實現了透明的zero-copy。
容量能夠按需增加。
讀和寫使用了不一樣的索引指針。
支持鏈式調用。
支持引用計數與池化。
能夠被用戶自定義的緩衝區類型擴展。函數
FileChannel:文件通道,用於文件的讀和寫
DatagramChannel:用於 UDP 鏈接的接收和發送
SocketChannel:把它理解爲 TCP 鏈接通道,簡單理解就是 TCP 客戶端
ServerSocketChannel:TCP 對應的服務端,用於監聽某個端口進來的請求oop
SocketChannel。它能夠看做是 socket 的一個完善類,除了提供 Socket 的相關功能外,還提供了許多其餘特性,如後面要講到的向選擇器註冊的功能。
Socket相關的類圖:
它相似於文件描述符,簡單地來講它表明了一個實體(如一個硬件設備、文件、Socket或者一個可以執行一個或多個不一樣的I/O操做的程序組件)。你能夠從一個Channel中讀取數據到緩衝區,也能夠將一個緩衝區中的數據寫入到Channel。
Netty中處處都充滿了 異步與事件驅動,而回調函數正是用於響應事件以後的操做。因爲異步會直接返回一個結果,因此Netty提供了ChannelFuture(實現了java.util.concurrent.Future)來做爲異步調用返回的佔位符,真正的結果會在將來的某個時刻完成,到時候就能夠經過ChannelFuture對其進行訪問,每一個Netty的出站I/O操做都將會返回一個ChannelFuture。
Selector 創建在非阻塞的基礎之上,你們常常聽到的多路複用世界中指的就是它,用於實現一個線程管理多個 Channel。
對於 Selector,咱們還須要很是熟悉如下幾個方法:
1. select()
調用此方法,會將上次 select 以後的準備好的 channel 對應的 SelectionKey 複製到 selected set 中。若是沒有任何通道準備好,這個方法會阻塞,直到至少有一個通道準備好。
2. electNow()
功能和 select 同樣,區別在於若是沒有準備好的通道,那麼此方法會當即返回 0。
3. select(long timeout)
看了前面兩個,這個應該很好理解了,若是沒有通道準備好,此方法會等待一會
4. wakeup()
這個方法是用來喚醒等待在 select() 和 select(timeout) 上的線程的。若是 wakeup() 先被調用,此時沒有線程在 select 上阻塞,那麼以後的一個 select() 或 select(timeout) 會當即返回,而不會阻塞,固然,它只會做用一次。