Kafka是由Apache軟件基金會開發的一個開源流處理平臺,被普遍地應用在數據緩衝、異步通訊、聚集日誌、系統解耦等方面。相比較於其餘常見消息系統,Kafka在保障了大部分功能特性的同時,還在高吞吐、低延遲等方面有很突出的表現。這篇文章不一樣於其餘介紹Kafka使用或實現的文章,只是談談Kafka用了什麼「黑科技」使他在性能方面有這麼突出的表現。web
消息順序寫入磁盤
磁盤大多數都仍是機械結構(SSD不在討論的範圍內),若是將消息以隨機寫的方式存入磁盤,就須要按柱面、磁頭、扇區的方式尋址,尋址是一個「機械動做」也最耗時。爲了提升讀寫硬盤的速度,Kafka就是使用順序I/O。
網絡
圖 1 Kafka順序IOapp
上圖中,每一個partition就是一個文件,每條消息都被append 到該 partition 中,屬於順序寫磁盤,所以效率很是高。這種方法有一個缺陷—— 沒有辦法刪除數據 ,因此Kafka是不會刪除數據的,它會把全部的數據都保留下來,每一個消費者(Consumer)對每一個Topic都有一個offset用來表示讀取到了第幾條數據 。dom
關於磁盤順序讀寫和隨機讀寫的性能,引用一組Kafka官方給出的測試數據(Raid-5,7200rpm):異步
Sequence I/O: 600MB/ssocket
Random I/O: 100KB/s性能
因此經過只作Sequence I/O,給Kafka帶來了性能的極大提高。測試
Zero Copy
考慮一個web程序讀取文件內容並傳輸到網絡的場景,實現的核心代碼以下:
spa
圖 2 普通read方法操作系統
雖然只是兩個調用,但卻通過了4次copy,其中有2次cpu copy,還有屢次用戶態與內核態的上下文切換,這會加劇cpu的負擔,而零拷貝就是爲了解決這種低效。
減小拷貝次數的一種方法是調用mmap()來代替read()調用:
應用程序調用mmap(),磁盤上的數據會經過DMA被拷貝到內核緩衝區,接着操做系統會把這段內核緩衝區與應用程序共享,這樣就不須要把內核緩衝區的內容往用戶空間拷貝。應用程序再調用write(),操做系統直接將內核緩衝區的內容拷貝到socket緩衝區中,最後再把數據發到網卡去。
圖 3 mmap方法
使用mmap能夠減小一次cpu copy,但也會遇到一些陷阱,當你的程序map了一個文件,可是當這個文件被另外一個進程截斷(truncate)時, write系統調用會由於訪問非法地址而被SIGBUS信號終止。一般能夠經過,爲SIGBUS信號創建信號處理程序或使用文件租憑(file leasing)的方式去解決,這裏就再也不贅述了。
從2.1版內核開始,Linux引入了sendfile來簡化操做
圖 4 sendfile方法
sendfile() 方法引起 DMA 引擎將文件內容拷貝到一個讀取緩衝區(DMA copy)而後由內核將數據拷貝到socket buffer(cpu copy)最後再拷貝到網卡(DMA copy)使用sendfile不只減小了數據拷貝的次數,還減小了上下文切換,數據傳送始終只發生在kernel space
聊到這裏,sendfile至少還須要一次cpu copy,那麼這一步能不能省去呢?爲了消除內核完成的全部數據複製,咱們須要一個支持收集(gather)操做的網絡接口。同時,在內核版本2.4中,也修改了套接字緩衝區描述符以適應零拷貝要求。 這種方法不只減小了多個上下文切換,還徹底取消了cpu copy。
圖 5 sendfile方法(DMA gather)
sendfile系統調用利用DMA引擎將文件內容拷貝到內核緩衝區去,而後將帶有文件位置和長度信息的緩衝區描述符添加socket緩衝區去,這一步不會將內核中的數據拷貝到socket緩衝區中,DMA引擎會將內核緩衝區的數據拷貝到協議引擎中去,避免了最後一次CPU拷貝。
零拷貝技術很是廣泛,JAVA的transferTo、transferFrom方法就是Zero Copy。