天天看點

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Netty 源碼:從“點”(領域知識)的角度剖析

目錄

Netty 源碼:從“點”(領域知識)的角度剖析

一、Netty 怎麼切換三種I/O 模式

• 什麼是經典的三種I/O 模式

• Netty 對三種I/O 模式的支援

• 為什麼Netty 僅支援NIO 了?

• 為什麼Netty 有多種NIO 實作?

• NIO 一定優于BIO 麼?

• 源碼解讀Netty 怎麼切換I/O 模式?

二、Netty 如何支援三種Reactor

• 什麼是Reactor 及三種版本

​• 如何在Netty 中使用Reactor 模式

• 解析Netty 對Reactor 模式支援的常見疑問

三、TCP 粘包/半包解決方法

• 什麼是粘包和半包?

​• 為什麼TCP 應用中會出現粘包和半包現象?

• 解決粘包和半包問題的幾種常用方法

• Netty 對三種常用封幀方式的支援

• 解讀Netty 處理粘包、半包的源碼

四、常用的“二次”編解碼方式

• 為什麼需要“二次”解碼?

• 常用的“二次”編解碼方式

• 選擇編解碼方式的要點

• Protobuf 簡介與使用

• 源碼解讀:Netty 對二次編解碼的支援

一、Netty 怎麼切換三種I/O 模式

• 什麼是經典的三種I/O 模式

阻塞與非阻塞:

阻塞:沒有資料傳過來時,讀會阻塞直到有資料;緩沖區滿時,寫操作也會阻塞。

非阻塞遇到這些情況,都是直接傳回。

同步與異步:

資料就緒後需要自己去讀是同步,資料就緒直接讀好再回調給程式是異步。

BIO (阻塞I/O)

NIO (非阻塞I/O)

AIO(異步I/O)

• Netty 對三種I/O 模式的支援

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• 為什麼Netty 僅支援NIO 了?

1.為什麼不建議(deprecate)阻塞I/O(BIO/OIO)?

連接配接數高的情況下:阻塞-> 耗資源、效率低

2.為什麼删掉已經做好的AIO 支援?

Windows 實作成熟,但是很少用來做伺服器。

3.Linux 常用來做伺服器,但是AIO 實作不夠成熟。

Linux 下AIO 相比較NIO 的性能提升不明顯。

• 為什麼Netty 有多種NIO 實作?

通用的NIO 實作(Common)在Linux 下也是使用epoll,為什麼自己單獨實作?

實作得更好!

• Netty 暴露了更多的可控參數,例如:

• JDK 的NIO 預設實作是水準觸發

• Netty 是邊緣觸發(預設)和水準觸發可切換

• Netty 實作的垃圾回收更少、性能更好

• NIO 一定優于BIO 麼?

• BIO 代碼簡單。

• 特定場景:連接配接數少,并發度低,BIO 性能不輸NIO。

• 源碼解讀Netty 怎麼切換I/O 模式?

以Netty源碼中的echoservice為例

Nio切換至Oio僅僅需要兩三行代碼

1.切換開發模式

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

源碼例子:NioEventLoop.run() 是一個Reactor模式的實作,就是一種開發模式

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

2.切換channel

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

源碼例子:源碼中比如ServerSocketChannel就是使用:工廠模式+泛型+反射實作

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

獲得泛型的構造器,在建立連接配接的時候會反射出對象 

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

二、Netty 如何支援三種Reactor

• 什麼是Reactor 及三種版本

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

ThreadThread-Per-Connection 模式:

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Reactor 模式V1:單線程

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Reactor 模式V2:多線程

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Reactor 模式V3:主從多線程

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• 如何在Netty 中使用Reactor 模式

Reactor單線程多線程模式差別主要是構造方法中傳入的線程數,如果不傳預設根據目前機器cpu核數計算

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• 解析Netty 對Reactor 模式支援的常見疑問

1.Netty 如何支援主從Reactor 模式的?

主要看這個group中set的兩個主從EventLoopGroup,其中主group主要是維護在AbstractBootstrap的成員變量中

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

使用,這個父Socket可以用于建立子channel

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

2.Netty 給Channel 配置設定NIO event loop 的規則是什麼

主要看EventLoopGroup.register()

這個是NioEventLoop的實作

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析
Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

接下來看next(),主要是個政策模式(多種算法)

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

第一種,取模

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

第二種在2的幂次方時候,才能用,位運算

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

3.通用模式的NIO 實作多路複用器是怎麼跨平台的

主要看NioEventLoopGroup

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

第一個走環境變量,第二個走SPI機制,查jar包的META-INF/services目錄

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

可以看到跨平台的邏輯了

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

三、TCP 粘包/半包解決方法

• 什麼是粘包和半包?

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• 為什麼TCP 應用中會出現粘包和半包現象?

粘包的主要原因:

• 發送方每次寫入資料< 套接字緩沖區大小

• 接收方讀取套接字緩沖區資料不夠及時

半包的主要原因:

• 發送方寫入資料> 套接字緩沖區大小

• 發送的資料大于協定的MTU(Maximum Transmission Unit,最大傳輸單元),必須拆包

根本原因:

TCP 是流式協定,消息無邊界。

提醒:UDP 像郵寄的包裹,雖然一次運輸多個,但每個包裹都有“界限”,一個一個簽收,

是以無粘包、半包問題。

• 解決粘包和半包問題的幾種常用方法

解決問題的根本手段:找出消息的邊界:

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• Netty 對三種常用封幀方式的支援

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

• 解讀Netty 處理粘包、半包的源碼

主要看ByteToMessageDecoder.channelRead()

先判斷msg是不是ByteBuf,即netty處理過的資料,如果不是說明是第一筆,就直接傳給資料積累器

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

否則走追加

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

解碼核心邏輯就是這個抽象方法

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

FixedLengthFrameDecoder就是剛才介紹的固定邊緣長度的邏輯,看代碼很容易了解,半包不處理,粘包隻處理固定長度的

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

四、常用的“二次”編解碼方式

• 為什麼需要“二次”解碼?

那麼我們在項目中,除了可選的的壓縮解壓縮之外,還需要一層解碼,因為一次解碼的結

果是位元組,需要和項目中所使用的對象做轉化,友善使用,這層解碼器可以稱為“二次解

碼器”,相應的,對應的編碼器是為了将Java 對象轉化成位元組流友善存儲或傳輸。

當然也不推薦合并一次、二次解碼,因為沒有分層、且耦合度太高

• 常用的“二次”編解碼方式

• Java 序列化

• Marshaling

• XML

• JSON

• MessagePack

• Protobuf

• 其他

• 選擇編解碼方式的要點

空間:編碼後占用空間

時間:編解碼速度

可讀性

多語言(Java、C、Python 等)的支援

• Protobuf 簡介與使用

• Protobuf 是一個靈活的、高效的用于序列化資料的協定。

• 相比較XML 和JSON 格式,Protobuf 更小、更快、更便捷。

• Protobuf 是跨語言的,并且自帶了一個編譯器(protoc),隻需要用它進行編譯,可

以自動生成Java、python、C++ 等代碼,不需要再寫其他代碼。

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

Netty源碼剖析與實戰-第二周Netty 源碼:從“點”(領域知識)的角度剖析

備注:本文皆轉自極客時間-netty源碼剖析與實戰,本人僅作整理和部分解讀,用于記錄和記憶留存!