天天看點

Netty源碼分析之設計模式在Netty架構中的應用

作者:Civen

如果你對設計模式不是很熟悉,建議補習一下設計模式再來閱讀本章的内容。本章主要分析設計模式的應用案例,不對設計模式本身做詳細講解。

1.1 單例模式源碼舉例

單例模式要點回顧如下。

(1)一個類在任何情況下隻有一個對象,并提供一個全局通路點。

(2)延遲建立。

(3)避免線程安全問題。

Netty中的單例模式應用非常廣泛,單例模式本身也比較簡單,下面列舉MqttEncoder類,采用餓漢式單例的寫法。餓漢式單例最大的優點就是簡單,絕對線程安全。具體代碼如下。

Netty源碼分析之設計模式在Netty架構中的應用
Netty源碼分析之設計模式在Netty架構中的應用

1.2 政策模式源碼舉例

政策模式要點回顧如下。

(1)封裝一系列可互相替換的算法家族。

(2)動态選擇某一個政策。

Netty在根據CPU核數配置設定線程數量的一個優化時,如果是2的平方則采用PowerOfTwoEventExecutorChooser來建立EventExecutorChooser,如果不是2的平方則采用GenericEventExecutorChooser來建立EventExecutorChooser,這裡用的是三元運算選擇政策,具體代碼如下。

Netty源碼分析之設計模式在Netty架構中的應用

1.3 裝飾者模式源碼舉例

裝飾者模式要點回顧如下。

(1)裝飾者和被裝飾者實作同一個接口。

(2)裝飾者通常繼承被裝飾者,同宗同源。

(3)動态修改、重載被裝飾者的方法。

從Netty的ByteBuf類結構圖可以看到,ByteBuf的直接實作類有五個,忽略WrappedByteBuf這個類,其實直接實作類有四個。為什麼要忽略掉WrappedByteBuf呢?因為它是ByteBuf裝飾者的基類,本身沒有任何實作功能。來看WrappedByteBuf的代碼,主要功能就是儲存被裝飾者的引用。

Netty源碼分析之設計模式在Netty架構中的應用

具體的裝飾者,繼承上面的裝飾者的頂級類,在自己的構造函數中接收ByteBuf的類型的參數,并把它傳遞給它的父類,使用者在調用裝飾者時,會把建立的最上面的四種待裝飾的元件類以構造方法的形式傳遞進去,整個體系就運作起來了。而且裝飾者可以按照自己的需求重寫父類的方法,或者在現在的基礎上添加新的方法調用進行增強。例如UnreleasableByteBuf類,重寫了release()方法,并傳回false表示UnreleasableByteBuf不支援被釋放。

Netty源碼分析之設計模式在Netty架構中的應用
Netty源碼分析之設計模式在Netty架構中的應用

再比如SimpLeakAwareByteBuf類,從字面意思來看,其實是一個記憶體洩露感覺的ByteBuf,同樣繼承自WrappedByteBuf。從構造方法來看,其構造參數多了一個ResourceLeak,主要用于對記憶體洩露的跟蹤。主要差異在于增加了release()方法,代碼如下。

Netty源碼分析之設計模式在Netty架構中的應用

看到上面的代碼,還是來關注release()方法。它調用release()方法,根據傳回結果額外增加了一個資源洩露的監控行為。

1.4 觀察者模式源碼舉例

觀察者模式要點回顧如下。

(1)兩個角色:觀察者和被觀察者。

(2)觀察者訂閱消息,被觀察者釋出消息。

(3)訂閱則能收到消息,取消訂閱則收不到消息。

Netty裡面的觀察者和被觀察者模式一般用Promise和Future來實作。項目中用得比較多的一個方法就是channel.writeAndFlush()方法。當調用channel.writeAndFlush()方法的時候,實際上就是建立了一個被觀察者ChannelFuture,來看源碼。

Netty源碼分析之設計模式在Netty架構中的應用

writeAndFlush()方法的傳回值是ChannelFuture。當調用ChannelFuture的addListener()方法的時候,其實就是往ChannelFuture中添加被一個ChannelPromise,繼續往下跟蹤源碼。

Netty源碼分析之設計模式在Netty架構中的應用
Netty源碼分析之設計模式在Netty架構中的應用

上面的writeAndFlush()方法還有一個重載方法,其中一個參數就是ChannelPromise,通常情況下Future和Promise是成對出現的。我們發現ChannelPromise就是ChannelFuture的子類,在Promise中定義了非常多的回調方法,提供給使用者去重載,使用者用自己的邏輯通常實作各種Listener接口來重載達到回調通知的目的。

1.5 疊代器模式源碼舉例

疊代器模式要點回顧如下。

(1)實作疊代器接口。

(2)實作對容器中的各個對象逐個通路的方法。

Netty裡面的CompositeByteBuf這個零拷貝的實作,就使用了疊代器模式。首先看一段代碼。

Netty源碼分析之設計模式在Netty架構中的應用

這段代碼把兩個ByteBuf添加到一起,forEachByte就是實作了疊代器模式。那麼為什麼說它是零拷貝呢?

我們找到forEachByte()的實作,在AbstractByteBuf裡面,有下面這樣一段代碼。

Netty源碼分析之設計模式在Netty架構中的應用
Netty源碼分析之設計模式在Netty架構中的應用

從readerIndex開始讀,讀到writeIndex。繼續跟進forEachByteAsc0()方法,檢視源碼。

Netty源碼分析之設計模式在Netty架構中的應用

繼續看getByte()方法的實作,找到CompositeByteBuf類的實作。

Netty源碼分析之設計模式在Netty架構中的應用

先找到Index對應的Component,然後疊代的時候直接傳回Component的Byte内容,就實作了零拷貝。其他的ByteBuf如果疊代的話,可能會把所有的資料都拷貝一遍。

1.6 責任鍊模式源碼舉例

責任鍊是指多個對象都有機會處理同一個請求,進而避免請求的發送者和接收者之間的耦合關系。然後,将這些對象連成一條鍊,并且沿着這條鍊往下傳遞請求,直到有一個對象可以處理它為止。在處理過程中,每個對象隻處理它自己關心的那一部分,不相關的部分可以繼續往下傳遞,直到鍊中的某個對象不想處理,可以将請求終止或丢棄。

責任鍊模式要點回顧如下。

(1)需要有一個頂層責任處理接口。

(2)需要有動态建立鍊、添加和删除責任處理器的接口。

(3)需要有上下文機制。

(4)需要有責任終止機制。

Netty的Pipeline就是采用了責任鍊設計模式,底層采用雙向連結清單的資料結構,将鍊上的各個處理器串聯起來。用戶端每一個請求到來,Netty都認為是Pipeline中所有的處理器都有機會處理它。是以,對于入棧的請求,全部從頭節點開始往後傳播,一直傳播到尾節點(來到尾節點的msg才會被釋放)。

Netty的責任鍊模式中的元件如下。

(1)責任處理器接口ChannelHandler。

(2)添加删除責任處理器的接口ChannelPipeline。

(3)上下文元件ChannelHandlerContext,可獲得使用者記需要的資料。

(4)終止責任鍊的ctx.fireXXX()方法,可終止傳播事件。

Pipeline中所有的Handler為頂級抽象接口,它規定了所有的Handler統一要有添加、移除、異常捕獲的行為。

Netty源碼分析之設計模式在Netty架構中的應用

Pipeline中的Handler被封裝進了上下文中。通過上下文可以輕松擷取目前節點所屬的Channel及其線程執行器。

Netty源碼分析之設計模式在Netty架構中的應用

責任終止機制包括如下内容。

(1)Pipeline中的任意一個節點,隻要不手動往下傳播,這個事件就會在目前節點終止傳播。

(2)對于入棧資料,預設會傳遞到尾節點進行回收,如果不進行下一步傳播,事件就會終止在目前節點。

(3)對于出棧資料,頭節點使用Unsafe對象,把資料寫回用戶端也意味着事件的終止。

底層事件的傳播使用的就是針對連結清單的操作,如AbstractChannelHandlerContext類的findContextInbound()方法。

Netty源碼分析之設計模式在Netty架構中的應用

1.7 工廠模式源碼舉例

工廠模式的要點就是将建立對象的邏輯封裝起來。

我們最先接觸ReflectiveChannelFactory就是專門用來建立Channel的工廠,它接收一個Class對象,然後調用Class的newInstance()方法,将建立好的對象傳回去。

Netty源碼分析之設計模式在Netty架構中的應用

Netty中的設計模式應用非常多,這裡不再一一列舉,希望大家花時間去探索,這樣才能夠有更多的收獲。本章旨在為大家打開深入探索Netty的大門,有不妥之處請糾正交流。

繼續閱讀