本文是我研究Kafka的一點心得,歡迎指出紕漏
更多訪問 個人博客
Kafka 做爲越發流行的流處理平臺,讓人好奇它爲什麼如此受人青睞,盛名之下無虛士,我從性能角度來探索 Kafka 的奧妙。html
首先,明確研究問題的方向。Kafka 是一個分佈式的流式數據平臺nginx
它的重要功能有:後端
在大數據需求背景下,Kafka 必然要對以上功能進行性能優化,性能的優化要點/瓶頸在於:緩存
利用操做系統的IO優化技術,脫離JVM的內存侷限。
爲何從操做系統提及呢?人們天天都在使用操做系統,反而廣泛忽略的操做系統的做用,讓咱們回想起來,操做系統的一大做用是消除硬件差別,爲用戶程序提供統一標準的API,由此,大部分人使用IO停留在調用系統的 read/write
,後端工程師則會更多瞭解 NIO
的 epoll/kqueue。僅此而已了嗎? 讓咱們看看下面優化策略:性能優化
實際上,現代的操做系統已經對磁盤IO作了複雜的優化,Linux 下有一個常見的縮寫名詞 vfs,即虛擬文件系統(virtual file system),它對內存與外存(磁盤)進行映射,使讀寫速度獲得提高,好比如下且不限於:網絡
sync
而定,用戶亦可主動調用 sync
,(PS:在Linux用戶都該知道拔U盤前執行一次sync
)以上的內存/磁盤映射的優化,這依賴於操做系統的預測策略,通常而言,每每是對磁盤的順序訪問,效率明顯更高。架構
操做系統除了自動完成以上的過程,還提供API mmap
給用戶主動映射文件到page cache
,系統會將這一片page cache
共享給用戶程序的內存,用戶程序沒必要提早 alloc memory
,直接讀取頁面緩存便可訪問數據。因而,在頻繁訪問一個大文件時,比起單純的write
,mmap
是一個更好的選擇,它減小了普通write
過程當中的用戶態與內核態的上下文切換次數(反覆複製緩存)。socket
上面咱們認識了磁盤緩存的優化策略,那麼對於另外一個被頻繁使用的IO對象—— socket ,如何優化呢分佈式
先認識 Linux 2.1起引入的 sendfile
系統調用,經過 sendfile
,咱們能夠把 page cache 的數據直接拷貝到 socket cache 中,僅僅將二者的文件描述符、數據的 offset 與 size 傳參給 sendfile
, DMA
引擎 (Direct Memory Access,直接內存存取)會將內核緩衝區的數據拷貝到協議引擎中去,不須要通過用戶態上下文,不須要用戶程序準備緩存,因而用戶緩存爲零,這就是 Zero-Copy 技術。性能
#include<sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
Zero-Copy 技術對 Java 程序來講無異於神兵,讓緩存的大小與速度脫離了 JVM
的侷限。
結合Kafka的使用場景——多個訂閱者拉取消息,消息被多個不一樣的消費者拉取,使用 Zero-Copy 技術,先調用 mmap
讀取磁盤數據到一份 page cache
,再屢次調用 sendfile
將一份 page cache
複製到不一樣的 socket cache
,整個複製過程在系統內核態中完成,極致的利用了操做系統的性能,瓶頸近乎限於硬件條件(磁盤讀寫、網絡速度)。
從人類實踐活動,咱們有一個經驗是:在一項事情中須要實現多個目標時,每每獲得在多個指標相互制衡的結果。
Kafka爲了解決網絡請求過多的問題,生產者會合並多條消息再提交,下降網絡IO的頻繁,以犧牲一點延遲換取更高的吞吐量。
在實踐中,咱們能夠爲生產者客戶端配置這個過程的參數,例如:設置消息大小上限爲64字節,延遲時間爲100毫秒,它的效果是:100毫秒內消息大小達到64字節就要當即發送,若是在100毫秒沒有達到64字節,可是100毫秒時間到了,也要發送緩存中的消息。
大數據場景下,分佈式的架構設計是必然
以前分析了單機環境的策略(操做系統、通訊IO),而後,在水平拓展上,Kafka有什麼性能優化策略呢?
作一個分佈式的消息系統,須要考慮什麼呢?
如何利用分佈式、多節點的優點,加強消息系統的吞吐量、容災能力、靈活擴容的能力...
讓咱們將思路抽象化,消息是流動的水,單機下是一條水管,多節點下是一片自來水網絡,爲了使消息的流動更加穩健,咱們得保證在消息流動的每個環節都有所保障
先羅列消息流動的每個環節
消息的狀態,在大多數消息隊列應用中,一般有三個等級:
消息的緩存
消息的順序
Kafka 如何應對這些環節呢?
Kafka 規則一,消息存儲在兩個維度上,虛擬上的分區(partition) 與 物理上的節點(broker),二者是多對一關係Kafka 規則二,一個 Topic 的一個分區同一時間最多隻有一個消費者線程拉取消息
Kafka 規則三,維護一個分區上消息緩存的消費者拉取進度
在水平拓展上,Kafka將一個 Topic 下從生產者收集到的消息存放到多個分區(partition)上,分區數大於等於Kafka節點數(brokers),每個分區最多分配一個消費者
對於消息的狀態問題,Kafka維護一個分區上的 offset 值,保存消息的消費進度,而這個進度須要由消費者提交
對於消息的時序問題,每個分區最多分配一個消費者,經過維護消費進度,就能夠保證同一個分區下的消息的時序性,由此:
直觀來看,分區(partition)、消費者(consumer)會發生一下幾種狀況的 rebalance
一開始有四個分區,一個消費者,四個分區的消息都須要被拉取,只好關聯同一個消費者
消費者有兩個了,能夠均衡分配
消費者四個了,更好了
消費者五個,爲了保存消息的時序性,維護一個offset值,一個分區最多隻能關聯一個消費者,因此這裏多出一個消費者空閒了
因爲業務要求,消費者有兩個分組了,消息的時序性是隻對一個分區、一個消費者分組生效的,這裏一個分區能夠關聯多個相互不一樣分組的消費者,維護多個 offset
除了水平拓展的分區,還要對總分區進行多個備份(Replicas),對一個分區設置一個 leader 多個 follower,由一個 leader 處理該分區的事務,follower 須要處於 ISR 狀態(In Sync Replicas),一旦 leader 故障,經過在備份最新的 follower 中產生新的 leader
mmap
, sendfile