《分佈式消息系統之Kafka》一、基本原理

兩張圖讀懂kafka應用:

image.png

 

image.png

1. Kafka 中的術語

  • broker:中間的kafka cluster,存儲消息,是由多個server組成的集羣。
  • topic:kafka給消息提供的分類方式。broker用來存儲不同topic的消息數據。
  • producer:往broker中某個topic裏面生產數據。
  • consumer:從broker中某個topic獲取數據。

2. 詳細說明

Broker
中間的kafka cluster,存儲消息,是由多個server組成的集羣。

image.png

topic與消息
kafka將所有消息組織成多個topic的形式存儲,而每個topic又可以拆分成多個partition,每個partition又由一個一個消息組成。每個消息都被標識了一個遞增序列號代表其進來的先後順序,並按順序存儲在partition中。

image.png

 

這樣,消息就以一個個id的方式,組織起來。

  • producer選擇一個topic,生產消息,消息會通過分配策略append到某個partition末尾。

  • consumer選擇一個topic,通過id指定從哪個位置開始消費消息。消費完成之後保留id,下次可以從這個位置開始繼續消費,也可以從其他任意位置開始消費。
    上面的id在kafka中稱爲offset,這種組織和處理策略提供瞭如下好處:

  • 消費者可以根據需求,靈活指定offset消費。

  • 保證了消息不變性,爲併發消費提供了線程安全的保證。每個
    consumer都保留自己的offset,互相之間不干擾,不存在線程安全問題。

  • 消息訪問的並行高效性。每個topic中的消息被組織成多個partition,partition均勻分配到集羣server中。生產、消費消息的時候,會被路由到指定partition,減少競爭,增加了程序的並行能力。

  • 增加消息系統的可伸縮性。每個topic中保留的消息可能非常龐大,通過partition將消息切分成多個子消息,並通過負責均衡策略將partition分配到不同server。這樣當機器負載滿的時候,通過擴容可以將消息重新均勻分配。

  • 保證消息可靠性。消息消費完成之後不會刪除,可以通過重置offset重新消費,保證了消息不會丟失。

  • 靈活的持久化策略。可以通過指定時間段(如最近一天)來保存消息,節省broker存儲空間。

  • 備份高可用性。消息以partition爲單位分配到多個server,並以partition爲單位進行備份。備份策略爲:1個leader和N個followers,leader接受讀寫請求,followers被動複製leader。leader和followers會在集羣中打散,保證partition高可用。

Partitions
每個Topics劃分爲一個或者多個Partition,並且Partition中的每條消息都被標記了一個sequential id ,也就是offset,並且存儲的數據是可配置存儲時間的

producer

  • producer生產消息需要如下參數:
  • topic:往哪個topic生產消息。
  • partition:往哪個partition生產消息。
  • key:根據該key將消息分區到不同partition。
  • message:消息。

     

    image.png

consumer
傳統消息系統有兩種模式:

  • 隊列
  • 發佈訂閱

kafka通過consumer group將兩種模式統一處理:每個consumer將自己標記consumer group名稱,之後系統會將consumer group按名稱分組,將消息複製並分發給所有分組,每個分組只有一個consumer能消費這條消息。如下圖:

 

image.png

於是推理出兩個極端情況:

  • 當所有consumer的consumer group相同時,系統變成隊列模式
  • 當每個consumer的consumer group都不相同時,系統變成發佈訂閱

注意

  • Consumer Groups 提供了topics和partitions的隔離, 如上圖Consumer Group A中的consumer-C2掛掉,consumer-C1會接收P1,P2,即一個consumer Group中有其他consumer掛掉後能夠重新平衡。如下圖:

  • 多consumer併發消費消息時,容易導致消息亂序,通過限制消費者爲同步,可以保證消息有序,但是這大大降低了程序的併發性。

kafka通過partition的概念,保證了partition內消息有序性,緩解了上面的問題。partition內消息會複製分發給所有分組,每個分組只有一個consumer能消費這條消息。這個語義保證了某個分組消費某個分區的消息,是同步而非併發的。如果一個topic只有一個partition,那麼這個topic併發消費有序,否則只是單個partition有序。

一般消息系統,consumer存在兩種消費模型:

  • push:優勢在於消息實時性高。劣勢在於沒有考慮consumer消費能力和飽和情況,容易導致producer壓垮consumer。
  • pull:優勢在可以控制消費速度和消費數量,保證consumer不會出現飽和。劣勢在於當沒有數據,會出現空輪詢,消耗cpu。

kafka採用pull,並採用可配置化參數保證當存在數據並且數據量達到一定量的時候,consumer端才進行pull操作,否則一直處於block狀態。kakfa採用整數值consumer position來記錄單個分區的消費狀態,並且單個分區單個消息只能被consumer group內的一個consumer消費,維護簡單開銷小。消費完成,broker收到確認,position指向下次消費的offset。由於消息不會刪除,在完成消費,position更新之後,consumer依然可以重置offset重新消費歷史消息。

消息發送語義
producer視角

  • 消息最多發送一次:producer異步發送消息,或者同步發消息但重試次數爲0。
  • 消息至少發送一次:producer同步發送消息,失敗、超時都會重試。
  • 消息發且僅發一次:後續版本支持。

consumer視角

  • 消息最多消費一次:consumer先讀取消息,再確認position,最後處理消息。
  • 消息至少消費一次:consumer先讀取消息,再處理消息,最後確認position。
  • 消息消費且僅消費一次。

注意:

  • 如果消息處理後的輸出端(如db)能保證消息更新冪等性,則多次消費也能保證exactly once語義。
  • 如果輸出端能支持兩階段提交協議,則能保證確認position和處理輸出消息同時成功或者同時失敗。
  • 在消息處理的輸出端存儲更新後的position,保證了確認position和處理輸出消息的原子性(簡單、通用)。

可用性
在kafka中,正常情況下所有node處於同步中狀態,當某個node處於非同步中狀態,也就意味着整個系統出問題,需要做容錯處理。

同步中代表了:

  • 該node與zookeeper能連通。
  • 該node如果是follower,那麼consumer position與leader不能差距太大(差額可配置)。

某個分區內同步中的node組成一個集合,即該分區的ISR。

kafka通過兩個手段容錯:

  • 數據備份:以partition爲單位備份,副本數可設置。當副本數爲N時,代表1個leader,N-1個followers,followers可以視爲leader的consumer,拉取leader的消息,append到自己的系統中

failover:

  • 當leader處於非同步中時,系統從followers中選舉新leader

  • 當某個follower狀態變爲非同步中時,leader會將此follower剔除ISR,當此follower恢復並完成數據同步之後再次進入 ISR。

另外,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,爲上面的分區批量選主。

一致性
上面的方案保證了數據高可用,有時高可用是體現在對一致性的犧牲上。如果希望達到強一致性,可以採取如下措施:

  • 禁用髒leader選舉,ISR沒有node時,寧可不提供服務也不要未完全同步的node。
  • 設置最小ISR數量min_isr,保證消息至少要被min_isr個node確認才能提交。

持久化
基於以下幾點事實,kafka重度依賴磁盤而非內存來存儲消息。

  • 硬盤便宜,內存貴
  • 順序讀+預讀取操作,能提高緩存命中率
  • 操作系統利用富餘的內存作爲pagecache,配合預讀取(read-ahead)+寫回(write-back)技術,從cache讀數據,寫到cache就返回(操作系統後臺flush),提高用戶進程響應速度
  • java對象實際大小比理想大小要大,使得將消息存到內存成本很高
  • 當堆內存佔用不斷增加時,gc抖動較大
  • 基於文件順序讀寫的設計思路,代碼編寫簡單
  • 在持久化數據結構的選擇上,kafka採用了queue而不是Btree
  • kafka只有簡單的根據offset讀和append操作,所以基於queue操作的時間複雜度爲O(1),而基於Btree操作的時間複雜度爲O(logN)
  • 在大量文件讀寫的時候,基於queue的read和append只需要一次磁盤尋址,而Btree則會涉及多次。磁盤尋址過程極大降低了讀寫性能

2. 參考文檔

kafka官方文檔
Kafka全解析
小白也能看懂的簡單明瞭kafka原理解析

3.原文鏈接

原文:https://blog.csdn.net/qq_29186199/article/details/80827085



作者:梅梅_19890321
鏈接:https://www.jianshu.com/p/0272d5e4ffad
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章