?前言
上篇文章講述了消息從生產(chǎn)到寫入到 Broker 的 partition 上背后發(fā)生的故事图仓,并提出了消息發(fā)送的網(wǎng)絡(luò)模型的問題。本篇文章我們來嘗試揭開其背后的神秘面紗猜扮,耐心看完你一定會(huì)有所收獲线罕。
文章概覽
1. Sender 線程的建連準(zhǔn)備階段和發(fā)送網(wǎng)絡(luò)請(qǐng)求兩階段盏档。
2. Selector 選擇器處理網(wǎng)絡(luò)請(qǐng)求過程。
Sender 線程的兩階段
上篇文章結(jié)尾提到了三個(gè)重要的方法把敢,分別是 ready()寄摆、send()、poll()技竟。其中 ready()和 send()可以理解為第一階段冰肴,即建連準(zhǔn)備階段;poll()可以理解為第二階段,即發(fā)送網(wǎng)絡(luò)請(qǐng)求階段熙尉。接下來對(duì)這兩階段做深入研究联逻。
ready()階段:?遍歷節(jié)點(diǎn)列表,查詢當(dāng)前是否已建立連接检痰,若已完成建連包归,則認(rèn)為該節(jié)點(diǎn)可用;若還未建連铅歼,則判斷該節(jié)點(diǎn)是否可以被連接公壤,若是則建連。對(duì)于不可建連和正在建連的節(jié)點(diǎn)暫時(shí)還不能參與網(wǎng)絡(luò)數(shù)據(jù)傳輸請(qǐng)求椎椰。
send()階段:?通過 ready()階段拿到了已經(jīng)完成建連的節(jié)點(diǎn)厦幅,然后遍歷節(jié)點(diǎn),判斷當(dāng)前節(jié)點(diǎn)是否可以被發(fā)送數(shù)據(jù)慨飘,若可以則將當(dāng)前節(jié)點(diǎn)對(duì)應(yīng)的 RequestChannel 加入到 InFlightRequest 雙端隊(duì)列中去确憨。為什么要將 RequestChannel 加入到一個(gè)雙端隊(duì)列中去呢?因?yàn)榉?wù)端為了保證服務(wù)端性能瓤的,一個(gè)服務(wù)端在同一時(shí)刻只能被一個(gè)客戶端請(qǐng)求連接休弃,如果上一個(gè)客戶端請(qǐng)求還未完成,則不允許新的客戶端請(qǐng)求連接圈膏。當(dāng)客戶端請(qǐng)求接收到服務(wù)端響應(yīng)后塔猾,將對(duì)應(yīng)的客戶端請(qǐng)求從 InFlightRequest 隊(duì)列中移除。
poll()階段:?通過 ready()和 send()兩階段稽坤,完成了數(shù)據(jù)準(zhǔn)備和可用節(jié)點(diǎn)檢查丈甸。在上一篇中我們介紹到客戶端是按照 Broker 分組,每組建立一個(gè)網(wǎng)絡(luò)連接請(qǐng)求尿褪,每個(gè)網(wǎng)絡(luò)連接請(qǐng)求管理多個(gè)網(wǎng)絡(luò)連接通道老虫,從而形成了一個(gè)連接同時(shí)與多個(gè) Broker 進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)傳輸。poll()方法采用了選擇器(Selector)模式來處理這種網(wǎng)絡(luò)模型茫多,其底層是使用 Java 的 NIO 來實(shí)現(xiàn)的。
簡單介紹下 Java NIO 的幾個(gè)組件忽刽,想要深入了解的同學(xué)通過 Google 去了解天揖。
SocketChannel:?客戶端網(wǎng)絡(luò)連接通道,在此通道上可進(jìn)行數(shù)據(jù)的讀寫操作跪帝,比如將數(shù)據(jù)寫入到通道中和將數(shù)據(jù)從通道中讀取出來操作今膊。
Selector:?選擇器,通道是需要注冊(cè)到 Selector 選擇器上的伞剑,同時(shí)在注冊(cè)后會(huì)返回一個(gè)選擇器建斑唬,Selector 會(huì)通過選擇器鍵來監(jiān)聽讀寫事件。
SelectorKey:?選擇器鍵,通道注冊(cè)到選擇器上恕刘,同時(shí)返回了選擇器鍵缤谎,從而使得選擇器鍵和通道建立了關(guān)系。
以上三者之間的關(guān)系如下:
當(dāng)有讀寫請(qǐng)求發(fā)生時(shí)褐着,Selector 可以通過 SelectorKey 拿到對(duì)應(yīng)的 SocketChannel坷澡,從而在 SocketChannel 上進(jìn)行數(shù)據(jù)的讀寫請(qǐng)求。
Selector 選擇器的實(shí)現(xiàn)原理
關(guān)于 Selector 選擇器含蓉,我們從兩個(gè)方面來介紹其背后發(fā)生了那些故事频敛,分別是 建連過程和讀寫操作流程。
Selector 建連過程分析
從上圖可以看出馅扣,首先打開一個(gè)客戶端連接 SocketChannel斟赚,然后對(duì) Socket 設(shè)置一些參數(shù),比如寫入數(shù)據(jù)大小差油、接受數(shù)據(jù)大小拗军、TCP 延遲等等參數(shù)。然后使用 SocketChannel 嘗試建立連接厌殉。建連完成后將 SocketChannel 注冊(cè)到 Selector 選擇器上食绿,并返回 SelectorKey。最后將 SocketChannel 包裝成 KafkaChannel公罕,并使用 SelectorKey 與 KafkaChannel 進(jìn)行關(guān)聯(lián)器紧;為啥會(huì)出現(xiàn) KafkaChannel 了呢?因?yàn)?Kafka 框架為了屏蔽 SocketChannel 內(nèi)部的細(xì)節(jié)操作楼眷,所有就對(duì) SocketChannel 進(jìn)行了一層包裝方便 Kafka 客戶端操作铲汪。
附上源碼供大家參考研究
Selector 選擇器讀寫操作流程
從上圖可以看出,以寫操作為例罐柳,客戶端輪詢到寫請(qǐng)求時(shí)掌腰,首先獲取寫請(qǐng)求對(duì)應(yīng)的 SelectorKey,從而拿到對(duì)應(yīng)的 KafkaChannel张吉;然后將要發(fā)送的數(shù)據(jù)寫入到 KafkaChannel 中齿梁;然后通過傳輸協(xié)議將數(shù)據(jù)交由底層的 SocketChannel;最后由 SocketChannel 將數(shù)據(jù)發(fā)送給 Broker肮蛹,完成數(shù)據(jù)的發(fā)送請(qǐng)求勺择。該過程中需要注意一個(gè)問題,Broker 在同一時(shí)間只能處理一個(gè)客戶端請(qǐng)求伦忠,如果當(dāng)前客戶端請(qǐng)求還沒被被處理完省核,下一個(gè)請(qǐng)求是不能被發(fā)送給服務(wù)端的。
總結(jié)
以上即為數(shù)據(jù)從客戶端發(fā)送到服務(wù)端背后相關(guān)的網(wǎng)絡(luò)操作故事昆码;到此气忠,關(guān)于生產(chǎn)者客戶端的相關(guān)操作暫且分析到這里邻储,關(guān)于客戶端冪等性、消息重發(fā)等問題我們?cè)诤竺鎸iT用篇幅來講解旧噪。下篇文章我們來分析一下消費(fèi)者端消費(fèi)消息背后的一些故事吨娜,敬請(qǐng)期待。
微信搜索公眾號(hào)【z小趙】舌菜,更多系列精彩文章等你解鎖