分佈式消息系統kafka的提供了一個生產者、緩衝區、消費者的模型html
kafka將全部消息組織成多個topic的形式存儲,而每一個topic又能夠拆分紅多個partition,每一個partition又由一個一個消息組成。每一個消息都被標識了一個遞增序列號表明其進來的前後順序,並按順序存儲在partition中。java
這樣,消息就以一個個id的方式,組織起來。node
這個id,在kafka中被稱爲offsetreact
這種組織和處理策略提供了以下好處:git
消息以partition爲單位分配到多個server,並以partition爲單位進行備份。備份策略爲:1個leader和N個followers,leader接受讀寫請求,followers被動複製leader。leader和followers會在集羣中打散,保證partition高可用github
producer生產消息須要以下參數:算法
根據kafka源碼,能夠根據不一樣參數靈活調整生產、分區策略apache
if topic is None throw Error p=None if partition Not None if partition < 0 Or partition >= numPartitions throw Error p=partition elif key Not None p=hash(key) % numPartitions else p=round-robin() % numPartitions send message to the partition p
上面是我翻譯的僞代碼,其中round-robin就是簡單輪詢,hash採用的是murmurhashapi
傳統消息系統有兩種模式:緩存
kafka經過consumer group將兩種模式統一處理
每一個consumer將本身標記consumer group名稱,以後系統會將consumer group按名稱分組,將消息複製並分發給全部分組,每一個分組只有一個consumer能消費這條消息。
因而推理出兩個極端狀況:
多consumer併發消費消息時,容易致使消息亂序
經過限制消費者爲同步,能夠保證消息有序,可是這大大下降了程序的併發性。
kafka經過partition的概念,保證了partition內消息有序性,緩解了上面的問題。partition內消息會複製分發給全部分組,每一個分組只有一個consumer能消費這條消息。這個語義保證了某個分組消費某個分區的消息,是同步而非併發的。若是一個topic只有一個partition,那麼這個topic併發消費有序,不然只是單個partition有序。
通常消息消息系統,consumer存在兩種消費模型:
kafka採用pull,並採用可配置化參數保證當存在數據而且數據量達到必定量的時候,consumer端才進行pull操做,不然一直處於block狀態
kakfa採用整數值consumer position來記錄單個分區的消費狀態,而且單個分區單個消息只能被consumer group內的一個consumer消費,維護簡單開銷小。消費完成,broker收到確認,position指向下次消費的offset。因爲消息不會刪除,在完成消費,position更新以後,consumer依然能夠重置offset從新消費歷史消息
producer視角
consumer視角
在kafka中,正常狀況下全部node處於同步中狀態,當某個node處於非同步中狀態,也就意味着整個系統出問題,須要作容錯處理
同步中表明了:
某個分區內同步中的node組成一個集合,即該分區的ISR
kafka經過兩個手段容錯:
另外,kafka有個保障:當producer生產消息時,只有當消息被全部ISR確認時,才表示該消息提交成功。只有提交成功的消息,才能被consumer消費
綜上所述:當有N個副本時,N個副本都在ISR中,N-1個副本都出現異常時,系統依然能提供服務
假設N副本全掛了,node恢復後會面臨同步數據的過程,這期間ISR中沒有node,會致使該分區服務不可用。kafka採用一種降級措施來處理:選舉第一個恢復的node做爲leader提供服務,以它的數據爲基準,這個措施被稱爲髒leader選舉。
因爲leader是主要提供服務的,kafka broker將多個partition的leader均分在不一樣的server上以均攤風險
每一個parition都有leader,若是在每一個partition內運行選主進程,那麼會致使產生很是多選主進程。kakfa採用一種輕量級的方式:從broker集羣中選出一個做爲controller,這個controller監控掛掉的broker,爲上面的分區批量選主
上面的方案保證了數據高可用,有時高可用是體如今對一致性的犧牲上。若是但願達到強一致性,能夠採起以下措施:
基於如下幾點事實,kafka重度依賴磁盤而非內存來存儲消息
在持久化數據結構的選擇上,kafka採用了queue而不是Btree
kafka在如下四點作了優化:
大量讀寫少許消息會致使性能較差,經過將消息聚合,能夠減小讀寫次數(減小隨機IO),增長單次讀寫數據量(增長順序IO)
普通狀況下,數據從磁盤傳輸到網絡須要經歷如下步驟:
利用sendfile系統調用,能夠簡化至:
減小了兩次拷貝步驟。在存在大量數據傳輸的操做時,會顯著提高性能
在大量文件讀寫的時候,基於queue的read和append只須要一次磁盤尋址,而Btree則會涉及屢次。磁盤尋址過程極大下降了讀寫性能
kafka server端採用與Mina同樣的網絡、線程模型。server端基於nio,採用1個acceptor線程接受tcp鏈接,並將鏈接分配給N個proccessor線程,proccessor線程執行具體的IO讀寫、邏輯處理操做。(注:相比較於這種模型,netty的N boss + N worker的模型更加靈活)
broker node在zookeeper中採用惟一id(整數)標識
/brokers/ids/[N] --> host:port 瞬時節點
此znode存儲了broker node的ip端口
/brokers/topics/[topic]/partitions/[N]/state --> leader,isr 瞬時節點
此znode存儲了該分區的leader id和isr列表(由id組成)
/consumers/[group_id]/ids/[customer_id] --> {"topic1": #streams, ..., "topicN": #streams} 瞬時節點
此znode存儲了指定consumer消費topic所使用的線程數
/consumers/[group_id]/offsets/[topic]/[N] --> offset 永久節點
consumer能夠經過三種方式管理offset:
此znode存儲了指定consumer在topic中最新consumer offset
/consumers/[group_id]/owners/[topic]/[N] --> consumer_id 瞬時節點
指定分區在某一時刻只能被全部consumer group中的某一個consumer消費,經過將consumer_id存在指定分區下,就能保證這時該分區只能被這個consumer消費
上面只是列出的最典型的znode,經過研究znode,能夠開發出一個kafka monitor,用來監控kafka數據消費情況,好比KafkaOffsetMonitor