《Netty實戰》-學習筆記1

《Netty實戰》-學習筆記1

Chapter 3 Netty的組件及設計

  • Channel:Socket,對應一個鏈接。java

    • 提供基本的 I/O 操做:如 bind()connect()read()write() 等,就是對 Java 中實現的 Socket 進行必定層次的封裝。
  • EventLoop:控制流、多線程處理、併發。數組

    • 在一個 EventLoopGroup 中包含一個或多個 EventLoop
    • 一個 EventLoop 在它的生命週期內只和一個 Thread 綁定。
    • 全部 EventLoop 處理的 I/O 事件都在它專有的 Thread 上被處理。
    • 一個 Channel 在它的生命週期內只註冊於一個 EventLoop
    • 一個 EventLoop 可能會被分配給一個或多個 Channel
  • ChannelFuture:異步通知。緩存

    • 在 Netty 中全部操做的是異步的,調用時不會當即返回結果,可是返回一個 Future 。可以在以後的某個時間點肯定結果,對 Future 添加 Listener 註冊回調,再操做完成後通知執行相應的回調。
  • ChannelHandler:在 Channel 上發生事件的處理器。服務器

    • 由網絡事件觸發的處理器,實際上能夠響應任何類型的動做。
    • ChannelInboundHandler:接收入站事件和數據。
    • ChannelOutboundHandler:數據出站處理器。
  • ChannelPipelineChannelHandler 鏈的容器。網絡

    • ChannelPipeline 就是一些 ChannelHandler 的編排順序,每一個 ChannelHandler 接收事件,執行所實現的處理邏輯,完畢後交給下個一個 ChannelHandler
    • 從客戶端角度來講,發送數據到服務器就是出站的。則經過 ChannelPipeline 的編排,在必定次序下調用 ChannelOutboundHandler 。響應的接收服務器發來數據就是入站的,在一個 ChannelPipeline 按順序調用 ChannelInboundHandler
    • 以下圖:客戶端操做系統從網絡上接收服務器發來數據放入緩存中,接下來,在 Netty 中從 ChannelPipeline 中從頭部開始按順序調用 ChannnelInboundHandler。從客戶端發送數據到服務器則是從 ChannelPipeline 的尾部開始執行,直到發送到網絡上。
  • ChannelHandlerContext:表明了 ChannelHandlerChannelPipeline 之間的綁定。多線程

    • 能夠用於獲取底層Channel
  • 兩種發送消息的方式:併發

    • 直接寫入 Channel 中:消息從 ChannelPipeline 的尾部開始流動。
    • 寫入到 ChannelHandler 相關聯的 ChannelHandlerContext 對象中: 消息從 ChannelPipeline 的下一個ChannelHandler開始流動。

Chapter 4 傳輸API

Channel

每一個 Channel 都會被分配一個 ChannelPipelineChannelConfig

  • ChannelConfig:對應 Channel 的配置設置,支持熱更新。
  • ChannelPipeline: 持有全部將應用於入站和出站數據以及事件的 ChannelHandler 實例,這些 ChannelHandler 實現了應用程序用於處理狀態變化以及數據處理的邏輯。

內置傳輸

名稱 描述
NIO io.netty.channel.socket.nio 使用 java.nio.channels 包做爲基礎——基於選擇器(select)
Epoll io.netty.channel.epoll 由 JNI 驅動的 epoll() 和非阻塞 IO。這個傳輸支持只有在Linux上可用的多種特性,如 SO_REUSEPORT,比NIO 傳輸更快,並且是徹底非阻塞的
OIO io.netty.channel.socket.oio 使用 java.net 包做爲基礎——使用阻塞流
Local io.netty.channel.local 能夠在 VM 內部經過管道進行通訊的本地傳輸
Embedded io.netty.channel.embedded Embedded 傳輸,容許使用 ChannelHandler 而又不須要一個真正的基於網絡的傳輸。這在測試 ChannelHandler 實現時很是有用
  • NIO 基於選擇器,每一個 Channel 在選擇器(Selector)上註冊,當 Selector 調用 select() 的時候是阻塞的,直到已經註冊的 Channel 中有相應的事件發生。
事件 描述
OP_ACCEPT 請求在接受新鏈接並建立 Channel 時得到通知
OP_CONNECT 請求在創建一個鏈接時得到通知
OP_READ 請求當數據已經就緒,能夠從 Channel 中讀取時得到通知
OP_WRITE 請求當能夠向 Channel 中寫更多的數據時得到通知。這處理了套接字緩衝區被徹底填滿時的狀況,這種狀況一般發生在數據的發送速度比遠程節點可處理的速度更快的時候

處理模型: 異步

  • Epoll NIO 是基於 Select 的,那麼 Epoll 如其名,則是基於 Linux 的 epoll 。好處不言而喻,提供比舊的 select 和 poll 更好的性能。 對應的則是 EpollEventLoopGroupEpollServerSocketChannelsocket

  • OIO 創建在 java.net 包下,不是異步的。可以經過設置 SO_TIMEOUT 來在單線程中處理多個鏈接。函數

  • Embedded Embedded 提供了能夠將一組 ChannelHandler 做爲幫助器嵌入到其餘的 ChannelHandler 內部。這樣能夠擴展一個 ChannelHandler 的功能,但又不須要修改其內部代碼。

Chapter 5 ByteBuf

ByteBuf工做原理

維護了兩個不一樣的索引:readerIndexwriterIndex ,一個用於讀取,一個用於寫入。當從 ByteBuf 讀取時,readerIndex 遞增。一樣當從 ByteBuf 寫入時, writerIndex 遞增。 若是 readerIndex 遞增到和 writerIndex 相同的值,那麼到達「可讀取數據的末尾」,這時再進行讀取則會像讀取超出數組末尾的數據同樣,拋出 IndexOutOfBoundsException。 另外,名稱以 read 或者 write 開頭的 ByteBuf 方法會推動相應的索引值,而以 set 或者 get 開頭的方法則不會(這樣只是在相應的索引值上進行操做)。

使用模式

在弄清楚 ByteBuf 的使用模式以前,應該理解 Java 中的直接緩衝區和非直接緩衝區:

  • 直接緩衝區:緩衝區創建在物理內存中,能夠提升效率。

  • 非直接緩衝區:緩衝區創建在 JVM 的內存中。

  • 堆緩衝區(backing 模式) ByteBuf 數據存儲在 JVM 的堆空間(非直接緩衝區)中。這種模式也被稱爲「支撐數組(backing array)」,可以在沒有使用池化的的狀況下提供快速的分配和釋放。

  • 直接緩衝區(direct 模式) 經過本地調用分配內存,直接分配的是物理內存。

  • 複合緩衝區 爲多個 ByteBuf 提供一個聚合視圖。

    • CompositeByteBuf:可以提供將多個緩衝區(每一個 ByteBuf 能夠是 backing 模式或者 direct 模式)表示爲單個合併緩衝區的虛擬表示。

字節級操做

  1. 隨機訪問索引 ByteBuf 的索引是從 0 開始到 capacity() - 1。 獲取每一個字節的方法則是:byte getByte(int i); 這樣倒不會改變 readerIndexwriterIndex 的值。 固然能夠經過:ByteBuf readerIndex(int index)ByteBuf writerIndex(int index)來手動移動讀取/寫入索引值。
  2. 順序訪問索引 有三個區域:
  3. 可丟棄字節 經過調用discardReadBytes()方法,能夠丟棄已經被讀過的字節以回收空間。是的,調用discardReadBytes()能夠確保可寫分段的最大化,可是有可能致使內存複製(可讀字節會移動到緩衝區的開始位置)。一樣的可丟棄字節空間也只是相應的更改了 writerIndex 的值,並不會保證空間中值的擦寫。
  4. 可讀字節 ByteBuf 的可讀字節分段(CONTENT)存儲了實際數據。任何名稱以 read 或者 skip 開頭的操做都將檢索或者跳過位於當前 readerIndex 的數據,而且將它增長已讀字節數。
  5. 可寫字節 可寫字節分段是指一個擁有未定義內容的、寫入就緒的內存區域。
  6. 索引管理 在 JDK 中 InputStream定義了mark(int readlimit)reset()方法,來標記流中指定位置,以及重置。在 ByteBuf 中也有相應的方法來標記和重置 ByteBuf 中的 readerIndexwriterIndex
    • markReaderIndex()
    • markWriterIndex()
    • resetReaderIndex()
    • resetWriterIndex() 還有clear()方法只是重置索引,且不會複製內存。
  7. 查找操做 最簡單的,可使用indexOf()方法。 或者使用ByteProcessor類。
  8. 派生緩衝區 提供了專門的方法來呈現器內容的視圖:
    • duplicate()
    • slice()
    • slice(int , int)
    • Unpooled.unmodifiableBuffer(...)
    • order(ByteOrder)
    • readSlice(int) 以上每一個方法都會返回一個新的ByteBuf實例,具備本身的讀索引、寫索引和標記索引。可是內部存儲是共享的。因此若是修改了它的內容,也同時修改了其對應的源實例。 若是要深拷貝得到真實副本,使用copy()或者copy(int ,int)函數。
  9. 讀/寫操做 兩種類別的讀/寫操做:
    • get()set()操做,從給定的索引值開始,且保持索引值不變。
    • read()write()操做,從給定的索引開始,且會根據以及訪問過的字節數堆索引進行調整。 get()方法:
      set()方法:
      read()方法:
      write()方法:
  10. 更多的操做

ByteBuf分配

  1. 按需分配:ByteBufAllocator接口 ByteBufAllocator的方法:
    能夠經過Channel或者ChannelHandlerContext獲取到一個ByteBufAllocator的引用。 Netty提供了兩種ByteBufAllocator的實現:
  • PooledByteBufAllocator:池化了ByteBuf的實例以提升性能而且最大限度的減小內存碎片。
  • UnpooledByteBufAllocator:不池化ByteBuf實例,可是每次被調用時會返回一個新的實例。
  1. Unpooled緩衝區 沒法獲取到ByteBufAllocator的引用時,有一個簡單的Unpooled工具類,提供靜態的輔助方法來建立未池化的ByteBuf實例。
  2. ByteBufUtil類 這個類提供了用於操做ByteBuf的靜態輔助方法。
相關文章
相關標籤/搜索