Netty 源碼解析 ——— ChannelConfig 和 Attribute

本文是Netty文集中“Netty 源碼解析”系列的文章藕筋。主要對Netty的重要流程以及類進行源碼解析葫督,以使得我們更好的去使用Netty叽赊。Netty是一個非常優(yōu)秀的網(wǎng)絡框架,對其源碼解讀的過程也是不斷學習的過程顺呕。

嗯枫攀,本文與其說是ChannelConfig、Attribute源碼解析株茶,不如說是對ChannelConfig以及Attribute結(jié)構(gòu)層次的分析来涨。因為這才是它們在Netty中使用到的重要之處。

ChannelConfig

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

而這一個NioServerSocketChannelConfig是當前NioServerSocketChannel配置屬性的集合。NioServerSocketChannelConfig主要用于對NioServerSocketChannel相關(guān)配置的設(shè)置(如僵闯,網(wǎng)絡的相關(guān)參數(shù)配置)卧抗,比如,配置Channel是否為非阻塞鳖粟、配置連接超時時間等等社裆。

下面我們來對NioServerSocketChannelConfig的結(jié)構(gòu)做個詳細介紹

NioServerSocketChannelConfig其實是一個ChannelConfig實例。ChannelConfig表示為一個Channel相關(guān)的配置屬性的集合向图。所以NioServerSocketChannelConfig就是針對于NioServerSocketChannel的配置屬性的集合泳秀。

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

DefaultChannelConfig作為ChannelConfig的默認實現(xiàn)憔狞,對ChannelConfig中的配置提供了默認值蝴悉。

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

取出我們程序設(shè)定的options(即簇抵,LinkedHashMap對象)庆杜,依次遍歷options中的key-value對,將其設(shè)置到channel中碟摆。

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

ChannelOption

這里壶愤,我們可以看到有個ChannelOption類,它允許以類型安全的方式去配置一個ChannelConfig馏鹤。支持哪一種ChannelOption取決于ChannelConfig的實際的實現(xiàn)并且也可能取決于它所屬的傳輸層的本質(zhì)公你。


可見ChannelOption是一個Consant擴展類,Consant是Netty提供的一個單例類假瞬,它能安全去通過’==’來進行比較操作陕靠。通過ConstantPool進行管理和創(chuàng)建。
常量由一個id和name組成脱茉。id:表示分配給常量的唯一數(shù)字剪芥;name:表示常量的名字。

ConstantPool

如上所說琴许,Constant是由ConstantPool來進行管理和創(chuàng)建的税肪,那么ConstantPool又是個什么樣的類了?

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

并通過??的代碼來實現(xiàn)的線程安全锻梳。主要通過ConcurrentHashMap的putIfAbsent來實現(xiàn)線程安全。

首先從constants中g(shù)et這個name對應的常量净捅,如果不存在則調(diào)用newConstant()來構(gòu)建這個常量tempConstant疑枯,然后在調(diào)用constants.putIfAbsent方法來實現(xiàn)“如果該name沒有存在對應的常量,則插入蛔六,否則返回該name所對應的常量荆永。(這整個的過程都是原子性的)”,因此我們是根據(jù)putIfAbsent方法的返回來判斷該name對應的常量是否已經(jīng)存在于constants中的国章。如果返回為null具钥,則說明當前創(chuàng)建的tempConstant就為name所對應的常量;否則液兽,將putIfAbsent返回的name已經(jīng)對應的常量值返回骂删。(注意,因為ConcurrentHashMap不會允許value為null的情況四啰,所以我們可以根據(jù)putIfAbsent返回為null則代表該name在此之前并未有對應的常量值)

ChannelOption類中屬性

好了桃漾,到目前為止,我們已經(jīng)知道ChannelOption是一個Constant的擴展拟逮,因此它可以由ConstantPool來管理和創(chuàng)建撬统。接下來,我們繼續(xù)來看看ChannelOption類中的一些重要屬性:

正如我們前面所說的敦迄,這個ConstantPool<ChannelOption<Object>> pool(即恋追,ChannelOption常量池)是ChannelOption的一個私有靜態(tài)成員屬性,用于管理和創(chuàng)建ChannelOption罚屋。

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

這些定義好的ChannelOption常量都已經(jīng)存儲數(shù)到ChannelOption的常量池(ConstantPool)中了脾猛。

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

好了,到目前為止跛溉,我們對Netty的ChannelOption的設(shè)置以及底層的實現(xiàn)已經(jīng)分析完了焊切,簡單的來說:Netty在初始化Channel時會構(gòu)建一個ChannelConfig對象扮授,而ChannelConfig是Channel配置屬性的集合。比如专肪,Netty在初始化NioServerSocketChannel的時候同時會構(gòu)建一個NioServerSocketChannelConfig對象刹勃,并將其賦值給NioServerSocketChannel的成員變量config,而這個config(NioServerSocketChannelConfig)維護了NioServerSocketChannel的所有配置屬性嚎尤。比如荔仁,NioServerSocketChannelConfig提供了setConnectTimeoutMillis方法來設(shè)置NioServerSocketChannel連接超時的時間贡这。
同時,程序可以通過ServerBootstrap或Boostrap的option(ChannelOption<T> option, T value)方法來實現(xiàn)配置的設(shè)置须眷。這里爷抓,我們通過ChannelOption來實現(xiàn)配置的設(shè)置,ChannelOption中已經(jīng)將常用的配置項預定義為了常量供我們直接使用定硝,同時ChannelOption的一個主要的功能就是讓特定的變量的值給類型化。因為從’ChannelOption<T> option’和’T value’可以看出,我們屬性的值類型T坪哄,是取決于ChannelOption的泛型的,也就屬性值類型是由屬性來決定的势篡。

Attribute

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

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

大體的流程是同ChannelOption一樣,這里就不過多贅述了瓷产。主要提及下一些關(guān)鍵點站玄。

如上面所說,Attribute就是一個屬性對象濒旦,這個屬性的名稱為AttributeKey<T> key株旷,而屬性的值為T value。
而AttributeKey也是Constant的一個擴展尔邓,因此也有一個ConstantPool來管理和創(chuàng)建晾剖,這和ChannelOption是類似的。

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

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

關(guān)于ChannelHandlerContext.attr(..) 和 Channel.attr(..)

Q:ChannelHandlerContext和Channel都提供了attr方法螟蒸,那么它們設(shè)置的屬性作用域有什么不同了盒使?
A:在Netty 4.1版本之前,它們兩設(shè)置的屬性作用域確實存在著不同七嫌,但從Netty 4.1版本開始少办,它們兩設(shè)置的屬性的作用域已經(jīng)完全相同了。

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

后記

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

參考

圣思園《精通并發(fā)與Netty》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懈叹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子分扎,更是在濱河造成了極大的恐慌澄成,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畏吓,死亡現(xiàn)場離奇詭異墨状,居然都是意外死亡,警方通過查閱死者的電腦和手機菲饼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門肾砂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宏悦,你說我怎么就攤上這事镐确“撸” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵源葫,是天一觀的道長诗越。 經(jīng)常有香客問我,道長息堂,這世上最難降的妖魔是什么嚷狞? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮荣堰,結(jié)果婚禮上床未,老公的妹妹穿的比我還像新娘。我一直安慰自己振坚,他們只是感情好薇搁,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屡拨,像睡著了一般只酥。 火紅的嫁衣襯著肌膚如雪褥实。 梳的紋絲不亂的頭發(fā)上呀狼,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音损离,去河邊找鬼哥艇。 笑死,一個胖子當著我的面吹牛僻澎,可吹牛的內(nèi)容都是我干的貌踏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼窟勃,長吁一口氣:“原來是場噩夢啊……” “哼祖乳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起秉氧,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤眷昆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汁咏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亚斋,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年攘滩,在試婚紗的時候發(fā)現(xiàn)自己被綠了帅刊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡漂问,死狀恐怖赖瞒,靈堂內(nèi)的尸體忽然破棺而出女揭,到底是詐尸還是另有隱情,我是刑警寧澤栏饮,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布田绑,位于F島的核電站,受9級特大地震影響抡爹,放射性物質(zhì)發(fā)生泄漏掩驱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一冬竟、第九天 我趴在偏房一處隱蔽的房頂上張望欧穴。 院中可真熱鬧,春花似錦泵殴、人聲如沸涮帘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽调缨。三九已至,卻和暖如春吆你,著一層夾襖步出監(jiān)牢的瞬間弦叶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工妇多, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伤哺,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓者祖,卻偏偏與公主長得像立莉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子七问,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容