Kafka 奪命連環(huán)11問(wèn)
干啥啥不行,看小說(shuō)第一名。這不,好好寫(xiě)了一篇文章。
最近整理了一下文章目錄,因?yàn)楹迷缰熬陀行值芨艺f(shuō)之前文章找不到,我也懶得整理,現(xiàn)在好好整了一下,發(fā)現(xiàn)有一篇文章寫(xiě)了一半我就放著了,抽空把他剛好補(bǔ)齊了一下,之前放著沒(méi)寫(xiě)大概是很難想到從哪里湊這么多問(wèn)題???
文章目錄的話去看公眾號(hào)菜單欄中間就有了,這樣大家以后方便點(diǎn)。
說(shuō)說(shuō)你對(duì)kafka的理解
kafka是一個(gè)流式數(shù)據(jù)處理平臺(tái),他具有消息系統(tǒng)的能力,也有實(shí)時(shí)流式數(shù)據(jù)處理分析能力,只是我們更多的偏向于把他當(dāng)做消息隊(duì)列系統(tǒng)來(lái)使用。
如果說(shuō)按照容易理解來(lái)分層的話,大致可以分為3層:
第一層是Zookeeper,相當(dāng)于注冊(cè)中心,他負(fù)責(zé)kafka集群元數(shù)據(jù)的管理,以及集群的協(xié)調(diào)工作,在每個(gè)kafka服務(wù)器啟動(dòng)的時(shí)候去連接到Zookeeper,把自己注冊(cè)到Zookeeper當(dāng)中
第二層里是kafka的核心層,這里就會(huì)包含很多kafka的基本概念在內(nèi):
record:代表消息
topic:主題,消息都會(huì)由一個(gè)主題方式來(lái)組織,可以理解為對(duì)于消息的一個(gè)分類
producer:生產(chǎn)者,負(fù)責(zé)發(fā)送消息
consumer:消費(fèi)者,負(fù)責(zé)消費(fèi)消息
broker:kafka服務(wù)器
partition:分區(qū),主題會(huì)由多個(gè)分區(qū)組成,通常每個(gè)分區(qū)的消息都是按照順序讀取的,不同的分區(qū)無(wú)法保證順序性,分區(qū)也就是我們常說(shuō)的數(shù)據(jù)分片sharding機(jī)制,主要目的就是為了提高系統(tǒng)的伸縮能力,通過(guò)分區(qū),消息的讀寫(xiě)可以負(fù)載均衡到多個(gè)不同的節(jié)點(diǎn)上
Leader/Follower:分區(qū)的副本。為了保證高可用,分區(qū)都會(huì)有一些副本,每個(gè)分區(qū)都會(huì)有一個(gè)Leader主副本負(fù)責(zé)讀寫(xiě)數(shù)據(jù),F(xiàn)ollower從副本只負(fù)責(zé)和Leader副本保持?jǐn)?shù)據(jù)同步,不對(duì)外提供任何服務(wù)
offset:偏移量,分區(qū)中的每一條消息都會(huì)根據(jù)時(shí)間先后順序有一個(gè)遞增的序號(hào),這個(gè)序號(hào)就是offset偏移量
Consumer group:消費(fèi)者組,由多個(gè)消費(fèi)者組成,一個(gè)組內(nèi)只會(huì)由一個(gè)消費(fèi)者去消費(fèi)一個(gè)分區(qū)的消息
Coordinator:協(xié)調(diào)者,主要是為消費(fèi)者組分配分區(qū)以及重平衡Rebalance操作
Controller:控制器,其實(shí)就是一個(gè)broker而已,用于協(xié)調(diào)和管理整個(gè)Kafka集群,他會(huì)負(fù)責(zé)分區(qū)Leader選舉、主題管理等工作,在Zookeeper第一個(gè)創(chuàng)建臨時(shí)節(jié)點(diǎn)/controller的就會(huì)成為控制器
第三層則是存儲(chǔ)層,用來(lái)保存kafka的核心數(shù)據(jù),他們都會(huì)以日志的形式最終寫(xiě)入磁盤(pán)中。
消息隊(duì)列模型知道嗎?kafka是怎么做到支持這兩種模型的?
對(duì)于傳統(tǒng)的消息隊(duì)列系統(tǒng)支持兩個(gè)模型:
- 點(diǎn)對(duì)點(diǎn):也就是消息只能被一個(gè)消費(fèi)者消費(fèi),消費(fèi)完后消息刪除
- 發(fā)布訂閱:相當(dāng)于廣播模式,消息可以被所有消費(fèi)者消費(fèi)
上面也說(shuō)到過(guò),kafka其實(shí)就是通過(guò)Consumer Group同時(shí)支持了這兩個(gè)模型。
如果說(shuō)所有消費(fèi)者都屬于一個(gè)Group,消息只能被同一個(gè)Group內(nèi)的一個(gè)消費(fèi)者消費(fèi),那就是點(diǎn)對(duì)點(diǎn)模式。
如果每個(gè)消費(fèi)者都是一個(gè)單獨(dú)的Group,那么就是發(fā)布訂閱模式。
實(shí)際上,Kafka通過(guò)消費(fèi)者分組的方式靈活的支持了這兩個(gè)模型。
能說(shuō)說(shuō)kafka通信過(guò)程原理嗎?
- 首先kafka broker啟動(dòng)的時(shí)候,會(huì)去向Zookeeper注冊(cè)自己的ID(創(chuàng)建臨時(shí)節(jié)點(diǎn)),這個(gè)ID可以配置也可以自動(dòng)生成,同時(shí)會(huì)去訂閱Zookeeper的brokers/ids路徑,當(dāng)有新的broker加入或者退出時(shí),可以得到當(dāng)前所有broker信息
- 生產(chǎn)者啟動(dòng)的時(shí)候會(huì)指定bootstrap.servers,通過(guò)指定的broker地址,Kafka就會(huì)和這些broker創(chuàng)建TCP連接(通常我們不用配置所有的broker服務(wù)器地址,否則kafka會(huì)和配置的所有broker都建立TCP連接)
- 隨便連接到任何一臺(tái)broker之后,然后再發(fā)送請(qǐng)求獲取元數(shù)據(jù)信息(包含有哪些主題、主題都有哪些分區(qū)、分區(qū)有哪些副本,分區(qū)的Leader副本等信息)
- 接著就會(huì)創(chuàng)建和所有broker的TCP連接
- 之后就是發(fā)送消息的過(guò)程
- 消費(fèi)者和生產(chǎn)者一樣,也會(huì)指定bootstrap.servers屬性,然后選擇一臺(tái)broker創(chuàng)建TCP連接,發(fā)送請(qǐng)求找到協(xié)調(diào)者所在的broker
- 然后再和協(xié)調(diào)者broker創(chuàng)建TCP連接,獲取元數(shù)據(jù)
- 根據(jù)分區(qū)Leader節(jié)點(diǎn)所在的broker節(jié)點(diǎn),和這些broker分別創(chuàng)建連接
- 最后開(kāi)始消費(fèi)消息
那么發(fā)送消息時(shí)如何選擇分區(qū)的?
主要有兩種方式:
- 輪詢,按照順序消息依次發(fā)送到不同的分區(qū)
- 隨機(jī),隨機(jī)發(fā)送到某個(gè)分區(qū)
如果消息指定key,那么會(huì)根據(jù)消息的key進(jìn)行hash,然后對(duì)partition分區(qū)數(shù)量取模,決定落在哪個(gè)分區(qū)上,所以,對(duì)于相同key的消息來(lái)說(shuō),總是會(huì)發(fā)送到同一個(gè)分區(qū)上,也是我們常說(shuō)的消息分區(qū)有序性。
很常見(jiàn)的場(chǎng)景就是我們希望下單、支付消息有順序,這樣以訂單ID作為key發(fā)送消息就達(dá)到了分區(qū)有序性的目的。
如果沒(méi)有指定key,會(huì)執(zhí)行默認(rèn)的輪詢負(fù)載均衡策略,比如第一條消息落在P0,第二條消息落在P1,然后第三條又在P1。
除此之外,對(duì)于一些特定的業(yè)務(wù)場(chǎng)景和需求,還可以通過(guò)實(shí)現(xiàn)Partitioner接口,重寫(xiě)configure和partition方法來(lái)達(dá)到自定義分區(qū)的效果。
好,那你覺(jué)得為什么需要分區(qū)?有什么好處?
這個(gè)問(wèn)題很簡(jiǎn)單,如果說(shuō)不分區(qū)的話,我們發(fā)消息寫(xiě)數(shù)據(jù)都只能保存到一個(gè)節(jié)點(diǎn)上,這樣的話就算這個(gè)服務(wù)器節(jié)點(diǎn)性能再好最終也支撐不住。
實(shí)際上分布式系統(tǒng)都面臨這個(gè)問(wèn)題,要么收到消息之后進(jìn)行數(shù)據(jù)切分,要么提前切分,kafka正是選擇了前者,通過(guò)分區(qū)可以把數(shù)據(jù)均勻地分布到不同的節(jié)點(diǎn)。
分區(qū)帶來(lái)了負(fù)載均衡和橫向擴(kuò)展的能力。
發(fā)送消息時(shí)可以根據(jù)分區(qū)的數(shù)量落在不同的Kafka服務(wù)器節(jié)點(diǎn)上,提升了并發(fā)寫(xiě)消息的性能,消費(fèi)消息的時(shí)候又和消費(fèi)者綁定了關(guān)系,可以從不同節(jié)點(diǎn)的不同分區(qū)消費(fèi)消息,提高了讀消息的能力。
另外一個(gè)就是分區(qū)又引入了副本,冗余的副本保證了Kafka的高可用和高持久性。
詳細(xì)說(shuō)說(shuō)消費(fèi)者組和消費(fèi)者重平衡?
Kafka中的消費(fèi)者組訂閱topic主題的消息,一般來(lái)說(shuō)消費(fèi)者的數(shù)量最好要和所有主題分區(qū)的數(shù)量保持一致最好(舉例子用一個(gè)主題,實(shí)際上當(dāng)然是可以訂閱多個(gè)主題)。
當(dāng)消費(fèi)者數(shù)量小于分區(qū)數(shù)量的時(shí)候,那么必然會(huì)有一個(gè)消費(fèi)者消費(fèi)多個(gè)分區(qū)的消息。
而消費(fèi)者數(shù)量超過(guò)分區(qū)的數(shù)量的時(shí)候,那么必然會(huì)有消費(fèi)者沒(méi)有分區(qū)可以消費(fèi)。
所以,消費(fèi)者組的好處一方面在上面說(shuō)到過(guò),可以支持多種消息模型,另外的話根據(jù)消費(fèi)者和分區(qū)的消費(fèi)關(guān)系,支撐橫向擴(kuò)容伸縮。
當(dāng)我們知道消費(fèi)者如何消費(fèi)分區(qū)的時(shí)候,就顯然會(huì)有一個(gè)問(wèn)題出現(xiàn)了,消費(fèi)者消費(fèi)的分區(qū)是怎么分配的,有先加入的消費(fèi)者時(shí)候怎么辦?
舊版本的重平衡過(guò)程主要通過(guò)ZK監(jiān)聽(tīng)器的方式來(lái)觸發(fā),每個(gè)消費(fèi)者客戶端自己去執(zhí)行分區(qū)分配算法。
新版本則是通過(guò)協(xié)調(diào)者來(lái)完成,每一次新的消費(fèi)者加入都會(huì)發(fā)送請(qǐng)求給協(xié)調(diào)者去獲取分區(qū)的分配,這個(gè)分區(qū)分配的算法邏輯由協(xié)調(diào)者來(lái)完成。
而重平衡Rebalance就是指的有新消費(fèi)者加入的情況,比如剛開(kāi)始我們只有消費(fèi)者A在消費(fèi)消息,過(guò)了一段時(shí)間消費(fèi)者B和C加入了,這時(shí)候分區(qū)就需要重新分配,這就是重平衡,也可以叫做再平衡,但是重平衡的過(guò)程和我們的GC時(shí)候STW很像,會(huì)導(dǎo)致整個(gè)消費(fèi)群組停止工作,重平衡期間都無(wú)法消息消息。
另外,發(fā)生重平衡并不是只有這一種情況,因?yàn)橄M(fèi)者和分區(qū)總數(shù)是存在綁定關(guān)系的,上面也說(shuō)了,消費(fèi)者數(shù)量最好和所有主題的分區(qū)總數(shù)一樣。
那只要消費(fèi)者數(shù)量、主題數(shù)量(比如用的正則訂閱的主題)、分區(qū)數(shù)量任何一個(gè)發(fā)生改變,都會(huì)觸發(fā)重平衡。
下面說(shuō)說(shuō)重平衡的過(guò)程。
重平衡的機(jī)制依賴消費(fèi)者和協(xié)調(diào)者之間的心跳來(lái)維持,消費(fèi)者會(huì)有一個(gè)獨(dú)立的線程去定時(shí)發(fā)送心跳給協(xié)調(diào)者,這個(gè)可以通過(guò)參數(shù)heartbeat.interval.ms來(lái)控制發(fā)送心跳的間隔時(shí)間。
- 每個(gè)消費(fèi)者第一次加入組的時(shí)候都會(huì)向協(xié)調(diào)者發(fā)送JoinGroup請(qǐng)求,第一個(gè)發(fā)送這個(gè)請(qǐng)求的消費(fèi)者會(huì)成為“群主”,協(xié)調(diào)者會(huì)返回組成員列表給群主
- 群主執(zhí)行分區(qū)分配策略,然后把分配結(jié)果通過(guò)SyncGroup請(qǐng)求發(fā)送給協(xié)調(diào)者,協(xié)調(diào)者收到分區(qū)分配結(jié)果
- 其他組內(nèi)成員也向協(xié)調(diào)者發(fā)送SyncGroup,協(xié)調(diào)者把每個(gè)消費(fèi)者的分區(qū)分配分別響應(yīng)給他們
那你跟我再具體講講分區(qū)分配策略?
主要有3種分配策略:
Range
不知道咋翻譯,這個(gè)是默認(rèn)的策略。大概意思就是對(duì)分區(qū)進(jìn)行排序,排序越靠前的分區(qū)能夠分配到更多的分區(qū)。
比如有3個(gè)分區(qū),消費(fèi)者A排序更靠前,所以能夠分配到P0\P1兩個(gè)分區(qū),消費(fèi)者B就只能分配到一個(gè)P2。
如果是4個(gè)分區(qū)的話,那么他們會(huì)剛好都是分配到2個(gè)。
但是這個(gè)分配策略會(huì)有點(diǎn)小問(wèn)題,他是根據(jù)主題進(jìn)行分配,所以如果消費(fèi)者組訂閱了多個(gè)主題,那就有可能導(dǎo)致分區(qū)分配不均衡。
比如下圖中兩個(gè)主題的P0\P1都被分配給了A,這樣A有4個(gè)分區(qū),而B(niǎo)只有2個(gè),如果這樣的主題數(shù)量越多,那么不均衡就越嚴(yán)重。
RoundRobin
也就是我們常說(shuō)的輪詢了,這個(gè)就比較簡(jiǎn)單了,不畫(huà)圖你也能很容易理解。
這個(gè)會(huì)根據(jù)所有的主題進(jìn)行輪詢分配,不會(huì)出現(xiàn)Range那種主題越多可能導(dǎo)致分區(qū)分配不均衡的問(wèn)題。
P0->A,P1->B,P1->A。。。以此類推
Sticky
這個(gè)從字面看來(lái)意思就是粘性策略,大概是這個(gè)意思。主要考慮的是在分配均衡的前提下,讓分區(qū)的分配更小的改動(dòng)。
比如之前P0\P1分配給消費(fèi)者A,那么下一次盡量還是分配給A。
這樣的好處就是連接可以復(fù)用,要消費(fèi)消息總是要和broker去連接的,如果能夠保持上一次分配的分區(qū)的話,那么就不用頻繁的銷毀創(chuàng)建連接了。
來(lái)吧!如何保證消息可靠性?
消息可靠性的保證基本上我們都要從3個(gè)方面來(lái)闡述(這樣才比較全面,無(wú)懈可擊)
生產(chǎn)者發(fā)送消息丟失
kafka支持3種方式發(fā)送消息,這也是常規(guī)的3種方式,發(fā)送后不管結(jié)果、同步發(fā)送、異步發(fā)送,基本上所有的消息隊(duì)列都是這樣玩的。
- 發(fā)送并忘記,直接調(diào)用發(fā)送send方法,不管結(jié)果,雖然可以開(kāi)啟自動(dòng)重試,但是肯定會(huì)有消息丟失的可能
- 同步發(fā)送,同步發(fā)送返回Future對(duì)象,我們可以知道發(fā)送結(jié)果,然后進(jìn)行處理
- 異步發(fā)送,發(fā)送消息,同時(shí)指定一個(gè)回調(diào)函數(shù),根據(jù)結(jié)果進(jìn)行相應(yīng)的處理
為了保險(xiǎn)起見(jiàn),一般我們都會(huì)使用異步發(fā)送帶有回調(diào)的方式進(jìn)行發(fā)送消息,再設(shè)置參數(shù)為發(fā)送消息失敗不停地重試。
acks=all,這個(gè)參數(shù)有可以配置0|1|all。
0表示生產(chǎn)者寫(xiě)入消息不管服務(wù)器的響應(yīng),可能消息還在網(wǎng)絡(luò)緩沖區(qū),服務(wù)器根本沒(méi)有收到消息,當(dāng)然會(huì)丟失消息。
1表示至少有一個(gè)副本收到消息才認(rèn)為成功,一個(gè)副本那肯定就是集群的Leader副本了,但是如果剛好Leader副本所在的節(jié)點(diǎn)掛了,F(xiàn)ollower沒(méi)有同步這條消息,消息仍然丟失了。
配置all的話表示所有ISR都寫(xiě)入成功才算成功,那除非所有ISR里的副本全掛了,消息才會(huì)丟失。
retries=N,設(shè)置一個(gè)非常大的值,可以讓生產(chǎn)者發(fā)送消息失敗后不停重試
kafka自身消息丟失
kafka因?yàn)橄?xiě)入是通過(guò)PageCache異步寫(xiě)入磁盤(pán)的,因此仍然存在丟失消息的可能。
因此針對(duì)kafka自身丟失的可能設(shè)置參數(shù):
replication.factor=N,設(shè)置一個(gè)比較大的值,保證至少有2個(gè)或者以上的副本。
min.insync.replicas=N,代表消息如何才能被認(rèn)為是寫(xiě)入成功,設(shè)置大于1的數(shù),保證至少寫(xiě)入1個(gè)或者以上的副本才算寫(xiě)入消息成功。
unclean.leader.election.enable=false,這個(gè)設(shè)置意味著沒(méi)有完全同步的分區(qū)副本不能成為L(zhǎng)eader副本,如果是true的話,那些沒(méi)有完全同步Leader的副本成為L(zhǎng)eader之后,就會(huì)有消息丟失的風(fēng)險(xiǎn)。
消費(fèi)者消息丟失
消費(fèi)者丟失的可能就比較簡(jiǎn)單,關(guān)閉自動(dòng)提交位移即可,改為業(yè)務(wù)處理成功手動(dòng)提交。
因?yàn)橹仄胶獍l(fā)生的時(shí)候,消費(fèi)者會(huì)去讀取上一次提交的偏移量,自動(dòng)提交默認(rèn)是每5秒一次,這會(huì)導(dǎo)致重復(fù)消費(fèi)或者丟失消息。
enable.auto.commit=false,設(shè)置為手動(dòng)提交。
還有一個(gè)參數(shù)我們可能也需要考慮進(jìn)去的:
auto.offset.reset=earliest,這個(gè)參數(shù)代表沒(méi)有偏移量可以提交或者broker上不存在偏移量的時(shí)候,消費(fèi)者如何處理。earliest代表從分區(qū)的開(kāi)始位置讀取,可能會(huì)重復(fù)讀取消息,但是不會(huì)丟失,消費(fèi)方一般我們肯定要自己保證冪等,另外一種latest表示從分區(qū)末尾讀取,那就會(huì)有概率丟失消息。
綜合這幾個(gè)參數(shù)設(shè)置,我們就能保證消息不會(huì)丟失,保證了可靠性。
OK,聊聊副本和它的同步原理吧?
Kafka副本的之前提到過(guò),分為L(zhǎng)eader副本和Follower副本,也就是主副本和從副本,和其他的比如Mysql不一樣的是,Kafka中只有Leader副本會(huì)對(duì)外提供服務(wù),F(xiàn)ollower副本只是單純地和Leader保持?jǐn)?shù)據(jù)同步,作為數(shù)據(jù)冗余容災(zāi)的作用。
在Kafka中我們把所有副本的集合統(tǒng)稱為AR(Assigned Replicas),和Leader副本保持同步的副本集合稱為ISR(InSyncReplicas)。
ISR是一個(gè)動(dòng)態(tài)的集合,維持這個(gè)集合會(huì)通過(guò)replica.lag.time.max.ms參數(shù)來(lái)控制,這個(gè)代表落后Leader副本的最長(zhǎng)時(shí)間,默認(rèn)值10秒,所以只要Follower副本沒(méi)有落后Leader副本超過(guò)10秒以上,就可以認(rèn)為是和Leader同步的(簡(jiǎn)單可以認(rèn)為就是同步時(shí)間差)。
另外還有兩個(gè)關(guān)鍵的概念用于副本之間的同步:
HW(High Watermark):高水位,也叫做復(fù)制點(diǎn),表示副本間同步的位置。如下圖所示,0~4綠色表示已經(jīng)提交的消息,這些消息已經(jīng)在副本之間進(jìn)行同步,消費(fèi)者可以看見(jiàn)這些消息并且進(jìn)行消費(fèi),4~6黃色的則是表示未提交的消息,可能還沒(méi)有在副本間同步,這些消息對(duì)于消費(fèi)者是不可見(jiàn)的。
LEO(Log End Offset):下一條待寫(xiě)入消息的位移
hw
副本間同步的過(guò)程依賴的就是HW和LEO的更新,以他們的值變化來(lái)演示副本同步消息的過(guò)程,綠色表示Leader副本,黃色表示Follower副本。
首先,生產(chǎn)者不停地向Leader寫(xiě)入數(shù)據(jù),這時(shí)候Leader的LEO可能已經(jīng)達(dá)到了10,但是HW依然是0,兩個(gè)Follower向Leader請(qǐng)求同步數(shù)據(jù),他們的值都是0。
然后,消息還在繼續(xù)寫(xiě)入,Leader的LEO值又發(fā)生了變化,兩個(gè)Follower也各自拉取到了自己的消息,于是更新自己的LEO值,但是這時(shí)候Leader的HW依然沒(méi)有改變。
此時(shí),F(xiàn)ollower再次向Leader拉取數(shù)據(jù),這時(shí)候Leader會(huì)更新自己的HW值,取Follower中的最小的LEO值來(lái)更新。
之后,Leader響應(yīng)自己的HW給Follower,F(xiàn)ollower更新自己的HW值,因?yàn)橛掷〉搅讼?,所以再次更新LEO,流程以此類推。
你知道新版本Kafka為什么拋棄了Zookeeper嗎?
我認(rèn)為可以從兩個(gè)個(gè)方面來(lái)回答這個(gè)問(wèn)題:
首先,從運(yùn)維的復(fù)雜度來(lái)看,Kafka本身是一個(gè)分布式系統(tǒng),他的運(yùn)維就已經(jīng)很復(fù)雜了,那除此之外,還需要重度依賴另外一個(gè)ZK,這對(duì)成本和復(fù)雜度來(lái)說(shuō)都是一個(gè)很大的工作量。
其次,應(yīng)該是考慮到性能方面的問(wèn)題,比如之前的提交位移的操作都是保存在ZK里面的,但是ZK實(shí)際上不適合這種高頻的讀寫(xiě)更新操作,這樣的話會(huì)嚴(yán)重影響ZK集群的性能,這一方面后來(lái)新版本中Kafka也把提交和保存位移用消息的方式來(lái)處理了。
另外Kafka嚴(yán)重依賴ZK來(lái)實(shí)現(xiàn)元數(shù)據(jù)的管理和集群的協(xié)調(diào)工作,如果集群規(guī)模龐大,主題和分區(qū)數(shù)量很多,會(huì)導(dǎo)致ZK集群的元數(shù)據(jù)過(guò)多,集群壓力過(guò)大,直接影響到很多Watch的延時(shí)或者丟失。
OK,最后一個(gè)大家都問(wèn)的問(wèn)題,Kafka為什么快?
嘿,這個(gè)我費(fèi),我背過(guò)好多次了!主要是3個(gè)方面:
順序IO
kafka寫(xiě)消息到分區(qū)采用追加的方式,也就是順序?qū)懭氪疟P(pán),不是隨機(jī)寫(xiě)入,這個(gè)速度比普通的隨機(jī)IO快非常多,幾乎可以和網(wǎng)絡(luò)IO的速度相媲美。
Page Cache和零拷貝
kafka在寫(xiě)入消息數(shù)據(jù)的時(shí)候通過(guò)mmap內(nèi)存映射的方式,不是真正立刻寫(xiě)入磁盤(pán),而是利用操作系統(tǒng)的文件緩存PageCache異步寫(xiě)入,提高了寫(xiě)入消息的性能,另外在消費(fèi)消息的時(shí)候又通過(guò)sendfile實(shí)現(xiàn)了零拷貝。
關(guān)于mmap和sendfile零拷貝我都專門(mén)寫(xiě)過(guò),可以看這里:阿里二面:什么是mmap?
批量處理和壓縮
Kafka在發(fā)送消息的時(shí)候不是一條條的發(fā)送的,而是會(huì)把多條消息合并成一個(gè)批次進(jìn)行處理發(fā)送,消費(fèi)消息也是一個(gè)道理,一次拉取一批次的消息進(jìn)行消費(fèi)。
并且Producer、Broker、Consumer都使用了優(yōu)化后的壓縮算法,發(fā)送和消息消息使用壓縮節(jié)省了網(wǎng)絡(luò)傳輸?shù)拈_(kāi)銷,Broker存儲(chǔ)使用壓縮則降低了磁盤(pán)存儲(chǔ)的空間。
我是艾小仙,過(guò)兩天準(zhǔn)備噴人。