Kafka如何實(shí)現(xiàn)高性能IO?
一、使用批量消息提升服務(wù)端處理能力
雖然kafka的sdk提供了單條消息發(fā)送,但實(shí)際上,Kafka 的客戶端 SDK 在實(shí)現(xiàn)消息發(fā)送邏輯的時(shí)候,采用了異步批量發(fā)送的機(jī)制;
當(dāng)你調(diào)用 send() 方法發(fā)送一條消息之后,無論你是同步發(fā)送還是異步發(fā)送,Kafka 都不會(huì)立即就把這條消息發(fā)送出去。它會(huì)先把這條消息,存放在內(nèi)存中緩存起來,然后選擇合適的時(shí)機(jī)把緩存中的所有消息組成一批,一次性發(fā)給 Broker
在 Kafka 的服務(wù)端批消息都不會(huì)被解開,一直是作為一條“批消息”來進(jìn)行處理的
在消費(fèi)時(shí),消息同樣是以批為單位進(jìn)行傳遞的,Consumer 從 Broker 拉到一批消息后,在客戶端把批消息解開,再一條一條交給用戶代碼處理
二、使用順序讀寫提升磁盤 IO 性能
順序讀寫相比隨機(jī)讀寫省去了大部分的尋址時(shí)間,它只要尋址一次,就可以連續(xù)地讀寫下去,所以說,性能要比隨機(jī)讀寫要好很多
Kafka 就是充分利用了磁盤的這個(gè)特性。它的存儲(chǔ)設(shè)計(jì)非常簡(jiǎn)單,對(duì)于每個(gè)分區(qū),它把從 Producer 收到的消息,順序地寫入對(duì)應(yīng)的 log 文件中,一個(gè)文件寫滿了,就開啟一個(gè)新的文件這樣順序?qū)懴氯ァOM(fèi)的時(shí)候,也是從某個(gè)全局的位置開始,也就是某一個(gè) log 文件中的某個(gè)位置開始,順序地把消息讀出來
三、利用緩存頁P(yáng)ageCache加速消息讀寫
PageCache
PageCache 是現(xiàn)代操作系統(tǒng)都具有的一項(xiàng)基本特性。通俗地說,PageCache 就是操作系統(tǒng)在內(nèi)存中給磁盤上的文件建立的緩存。無論我們使用什么語言編寫的程序,在調(diào)用系統(tǒng)的 API 讀寫文件的時(shí)候,并不會(huì)直接去讀寫磁盤上的文件,應(yīng)用程序?qū)嶋H操作的都是 PageCache,也就是文件在內(nèi)存中緩存的副本。
應(yīng)用程序?qū)懭胛募?/p>
操作系統(tǒng)會(huì)先把數(shù)據(jù)寫入到內(nèi)存中的 PageCache,然后再一批一批地寫到磁盤上
應(yīng)用程序讀取文件
有兩種情況:一種是 PageCache 中有數(shù)據(jù),那就直接讀?。涣硪环N情況是,PageCache 中沒有數(shù)據(jù),這時(shí)候操作系統(tǒng)會(huì)引發(fā)一個(gè)缺頁中斷,應(yīng)用程序的讀取線程會(huì)被阻塞,操作系統(tǒng)把數(shù)據(jù)從文件中復(fù)制到 PageCache 中,然后應(yīng)用程序再從 PageCache 中繼續(xù)把數(shù)據(jù)讀出來,這時(shí)會(huì)真正讀一次磁盤上的文件,這個(gè)讀的過程就會(huì)比較慢。
PageCache清理機(jī)制
用戶的應(yīng)用程序在使用完某塊 PageCache 后,操作系統(tǒng)并不會(huì)立刻就清除這個(gè) PageCache,而是盡可能地利用空閑的物理內(nèi)存保存這些 PageCache,除非系統(tǒng)內(nèi)存不夠用,操作系統(tǒng)才會(huì)清理掉一部分 PageCache。清理的策略一般是 LRU 或它的變種算法,它保留 PageCache 的邏輯是:優(yōu)先保留最近一段時(shí)間最常使用的那些 PageCache
kafka讀寫消息文件
kafka充分利用了 PageCache 的特性。一般來說,消息剛剛寫入到服務(wù)端就會(huì)被消費(fèi),按照 LRU 的“優(yōu)先清除最近最少使用的頁”這種策略,讀取的時(shí)候,對(duì)于這種剛剛寫入的 PageCache,命中的幾率會(huì)非常高。也就是說,大部分情況下,kafka消費(fèi)讀消息都會(huì)命中 PageCache,帶來的好處有兩個(gè):一個(gè)是讀取的速度會(huì)非??欤硗庖粋€(gè)是,給寫入消息讓出磁盤的 IO 資源,間接也提升了寫入的性能。
四、ZeroCopy:零拷貝技術(shù)
在消息服務(wù)端處理消費(fèi)的大致過程:
- 1、從文件中找到消息數(shù)據(jù),讀到內(nèi)存中;
- 2、把消息通過網(wǎng)絡(luò)發(fā)給客戶端。
這個(gè)過程中,數(shù)據(jù)實(shí)際上做了 2 次或者 3 次復(fù)制:
- 1、從文件復(fù)制數(shù)據(jù)到 PageCache 中,如果命中 PageCache,這一步可以省掉;
- 2、從 PageCache 復(fù)制到應(yīng)用程序的內(nèi)存空間中,也就是我們可以操作的對(duì)象所在的內(nèi)存;
- 3、從應(yīng)用程序的內(nèi)存空間復(fù)制到 Socket 的緩沖區(qū),這個(gè)過程就是我們調(diào)用網(wǎng)絡(luò)應(yīng)用框架的 API 發(fā)送數(shù)據(jù)的過程。
Kafka 使用零拷貝技術(shù)可以把這個(gè)復(fù)制次數(shù)減少一次,上面的 2、3 步驟兩次復(fù)制合并成一次復(fù)制。直接從 PageCache 中把數(shù)據(jù)復(fù)制到 Socket 緩沖區(qū)中,這樣不僅減少一次數(shù)據(jù)復(fù)制,更重要的是,由于不用把數(shù)據(jù)復(fù)制到用戶內(nèi)存空間,DMA 控制器可以直接完成數(shù)據(jù)復(fù)制,不需要 CPU 參與,速度更快