Netty 源碼解析 ——— ChannelConfig 和 Attribute

本文是Netty文集中“Netty 源碼解析”系列的文章。主要對Netty的重要流程以及類進行源碼解析,以使得我們更好的去使用Netty。Netty是一個非常優秀的網絡框架,對其源碼解讀的過程也是不斷學習的過程。

嗯,本文與其說是ChannelConfig、Attribute源碼解析,不如說是對ChannelConfig以及Attribute結構層次的分析。因為這才是它們在Netty中使用到的重要之處。

ChannelConfig

Netty 源碼解析 ——— 服務端啟動流程 (下)中說過,當我們在構建NioServerSocketChannel的時候同時會構建一個NioServerSocketChannelConfig對象賦值給NioServerSocketChannel的成員變量config。

而這一個NioServerSocketChannelConfig是當前NioServerSocketChannel配置屬性的集合。NioServerSocketChannelConfig主要用于對NioServerSocketChannel相關配置的設置(如,網絡的相關參數配置),比如,配置Channel是否為非阻塞、配置連接超時時間等等。

下面我們來對NioServerSocketChannelConfig的結構做個詳細介紹

NioServerSocketChannelConfig其實是一個ChannelConfig實例。ChannelConfig表示為一個Channel相關的配置屬性的集合。所以NioServerSocketChannelConfig就是針對于NioServerSocketChannel的配置屬性的集合。

ChannelConfig是Channel所需的公共配置屬性的集合,如,setAllocator(設置用于channel分配buffer的分配器)。而不同類型的網絡傳輸對應的Channel有它們自己特有的配置,因此可以通過擴展ChannelConfig來補充特有的配置,如,ServerSocketChannelConfig是針對基于TCP連接的服務端ServerSocketChannel相關配置屬性的集合,它補充了針對TCP服務端所需的特有配置的設置setBacklog、setReuseAddress、setReceiveBufferSize。

DefaultChannelConfig作為ChannelConfig的默認實現,對ChannelConfig中的配置提供了默認值。

接下來,我們來看一個設置ChannelConfig的流程:
serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);
我們可以在啟動服務端前通過ServerBootstrap來進行相關配置的設置,該選項配置會在Channel初始化時被獲取并設置到Channel中,最終會調用底層ServerSocket.setReuseAddress方法來完成配置的設置。
ServerBootstrap的init()方法:

取出我們程序設定的options(即,LinkedHashMap對象),依次遍歷options中的key-value對,將其設置到channel中。

首先對option和value進行校驗,其實就是進行非空校驗。
然后判斷對應的是哪個常量屬性,并進行相應屬性的設置。如果傳進來的ChannelOption不是已經設定好的常量屬性,則會打印一條警告級別的日志,告知這是未知的channel option。
Netty提供ChannelOption的一個主要的功能就是讓特定的變量的值給類型化。因為從’ChannelOption<T> option’和’T value’可以看出,我們屬性的值類型T,是取決于ChannelOption的泛型的,也就屬性值類型是由屬性來決定的。

ChannelOption

這里,我們可以看到有個ChannelOption類,它允許以類型安全的方式去配置一個ChannelConfig。支持哪一種ChannelOption取決于ChannelConfig的實際的實現并且也可能取決于它所屬的傳輸層的本質。


可見ChannelOption是一個Consant擴展類,Consant是Netty提供的一個單例類,它能安全去通過’==’來進行比較操作。通過ConstantPool進行管理和創建。
常量由一個id和name組成。id:表示分配給常量的唯一數字;name:表示常量的名字。

ConstantPool

如上所說,Constant是由ConstantPool來進行管理和創建的,那么ConstantPool又是個什么樣的類了?

ConstantPool是Netty提供的一個常量池類,它底層通過一個成員變量constants來維護所有的常量:
constants:底層就是Java的ConcurrentHashMap對象。

并通過??的代碼來實現的線程安全。主要通過ConcurrentHashMap的putIfAbsent來實現線程安全。

首先從constants中get這個name對應的常量,如果不存在則調用newConstant()來構建這個常量tempConstant,然后在調用constants.putIfAbsent方法來實現“如果該name沒有存在對應的常量,則插入,否則返回該name所對應的常量。(這整個的過程都是原子性的)”,因此我們是根據putIfAbsent方法的返回來判斷該name對應的常量是否已經存在于constants中的。如果返回為null,則說明當前創建的tempConstant就為name所對應的常量;否則,將putIfAbsent返回的name已經對應的常量值返回。(注意,因為ConcurrentHashMap不會允許value為null的情況,所以我們可以根據putIfAbsent返回為null則代表該name在此之前并未有對應的常量值)

ChannelOption類中屬性

好了,到目前為止,我們已經知道ChannelOption是一個Constant的擴展,因此它可以由ConstantPool來管理和創建。接下來,我們繼續來看看ChannelOption類中的一些重要屬性:

正如我們前面所說的,這個ConstantPool<ChannelOption<Object>> pool(即,ChannelOption常量池)是ChannelOption的一個私有靜態成員屬性,用于管理和創建ChannelOption。

同時,ChannelOption中將所有的與相關的配置項名稱都已常量形式定義好了。如:

這些定義好的ChannelOption常量都已經存儲數到ChannelOption的常量池(ConstantPool)中了。

注意,ChannelOption本身并不維護選項值的信息,它只是維護選項名字本身。比如,“public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");”??這只是維護了“SO_RCVBUF”這個選項名字的信息,同時泛型表示選擇值類型,即“SO_RCVBUF”選項值為Integer。

好了,到目前為止,我們對Netty的ChannelOption的設置以及底層的實現已經分析完了,簡單的來說:Netty在初始化Channel時會構建一個ChannelConfig對象,而ChannelConfig是Channel配置屬性的集合。比如,Netty在初始化NioServerSocketChannel的時候同時會構建一個NioServerSocketChannelConfig對象,并將其賦值給NioServerSocketChannel的成員變量config,而這個config(NioServerSocketChannelConfig)維護了NioServerSocketChannel的所有配置屬性。比如,NioServerSocketChannelConfig提供了setConnectTimeoutMillis方法來設置NioServerSocketChannel連接超時的時間。
同時,程序可以通過ServerBootstrap或Boostrap的option(ChannelOption<T> option, T value)方法來實現配置的設置。這里,我們通過ChannelOption來實現配置的設置,ChannelOption中已經將常用的配置項預定義為了常量供我們直接使用,同時ChannelOption的一個主要的功能就是讓特定的變量的值給類型化。因為從’ChannelOption<T> option’和’T value’可以看出,我們屬性的值類型T,是取決于ChannelOption的泛型的,也就屬性值類型是由屬性來決定的。

Attribute

一個attribute允許存儲一個值的引用。它可以被自動的更新并且是線程安全的。
其實Attribute就是一個屬性對象,這個屬性的名稱為AttributeKey<T> key,而屬性的值為T value。

我們可以通過程序ServerBootstrap或Boostrap的attr方法來設置一個Channel的屬性,如:
serverBootstrap.attr(AttributeKey.valueOf("userID"), UUID.randomUUID().toString());
當Netty底層初始化Channel的時候,就會將我們設置的attribute給設置到Channel中:

大體的流程是同ChannelOption一樣,這里就不過多贅述了。主要提及下一些關鍵點。

如上面所說,Attribute就是一個屬性對象,這個屬性的名稱為AttributeKey<T> key,而屬性的值為T value。
而AttributeKey也是Constant的一個擴展,因此也有一個ConstantPool來管理和創建,這和ChannelOption是類似的。

Channel類本身繼承了AttributeMap類,而AttributeMap它持有多個Attribute,這些Attribute可以通過AttributeKey來訪問的。所以,才可以通過channel.attr(key).set(value)的方式將屬性設置到channel中了(即,這里的attr方法實際上是AttributeMap接口中的方法)。

AttributeKey、Attribute、AttributeMap間的關系:
AttributeMap相對于一個map,AttributeKey相當于map的key,Attribute是一個持有key(AttributeKey)和value的對象。因此在map中我們可以通過AttributeKey key獲取Attribute,從而獲取Attribute中的value(即,屬性值)。

關于ChannelHandlerContext.attr(..) 和 Channel.attr(..)

Q:ChannelHandlerContext和Channel都提供了attr方法,那么它們設置的屬性作用域有什么不同了?
A:在Netty 4.1版本之前,它們兩設置的屬性作用域確實存在著不同,但從Netty 4.1版本開始,它們兩設置的屬性的作用域已經完全相同了。

Netty 4.1版本新特性及注意點
從上面的描述上,我們可以知道從Netty 4.1 開始 “ChannelHandlerContext.attr(..) == Channel.attr(..)”。即放入它們的attribute的作用域是一樣的了。每個Channel內部指保留一個AttributeMap。
而在Netty4.1之前,Channel內部保留有一個AttributeMap,而每個ChannelHandlerContext內部又保留有它們自己的AttributeMap,這樣通過Channel.attr()放入的屬性,是無法通過ChannelHandlerContext.attr()得到的,反之亦然。這種行為不僅令人困惑還會浪費內存。因此有了Netty 4.1將attr作用域統一的做法。

后記

若文章有任何錯誤,望大家不吝指教:)

參考

圣思園《精通并發與Netty》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容