一文讀懂消息隊列一些設計

高可用

經常使用的消息隊列的高可用是怎麼設計的呢?算法

消息隊列通常都有一個nameserver服務,用來檢測broker是否存活,或者處理能力上是否存在延遲。這樣在發送消息時就能夠規避將消息發送到宕機的broker上,也避免由於網絡等緣由消息處理失敗。網絡

那麼針對於以上兩種狀況,消息隊列如何保證高可用方案的呢?分佈式

多副本

每一個topic能夠設置幾個partition,每一個partition負責存儲一部分數據。kafka的broker集羣中,每臺機器存儲一些partition,存放一部分topic數據,這就實現了topic數據分佈在一個broker集羣上。設計

任何一個分佈式系統,內部都有一套多副本冗餘機制,多副本冗餘是任何一個分佈式系統具有的基本能力。code

kafka中每一個partition都有多個副本,其中一個副本是leader,其餘副本爲follower,leader和follower分佈在不一樣機器上。leader對外統一提供寫服務,leader接收到消息後follower副本會不停的和leader通訊,嘗試拉去最新數據,並持久化到本地磁盤。server

說到這裏不得不說下ISR,也就是保持同步的副本,表示了和leader始終保持同步的follower有哪些。blog

好比follower因爲fullgc形成本身卡頓,使得沒法及時從leader拉取數據,會致使這個follower數據比leader落後不少。
只要follower一直和leader保持同步關係,他們就處於同步關係。隊列

每一個partition都有一個ISR,這個ISR必定有leader,由於leader的數據永遠是最新的,而後就是和leader保持同步的follower,也會在ISR裏面。kafka

kafka中有個acks參數。是在producer裏面設置的,也就是客戶端設置的。同步

在向fafka集羣寫數據時,能夠設置這個acks參數,這個參數值有:0,1,all。

0:
意思是proucer在客戶端只要把消息發送出去,無論消息有沒有在partition leader上落盤就無論了。就認爲消息發送成功了。

1:
意思是producer生產的消息要確保partition leader寫入本地磁盤,就認爲成功了,而無論follower有沒有同步這條消息。
固然這個是kafka的默認設置。

all:
意思是partition leader接收到消息後,持久化到本地,還要求ISR列表中跟leader保持同步的那些follower要把消息持久了,纔算寫入成功。通常要求acks=all時,必須isr列表裏面有兩個以上的副本配合使用,起碼每一個leader有一個follower才行。

當broker回覆客戶端消息沒有寫入成功時,須要客戶端進行消息重發。

重試

消息發送時,通常存在這樣的方法:

for(; times < timesTotal; times++){
// send message
}

這裏是client發送消息時決定的重試次數,默認值爲3。重試能夠提升消息發送的成功率。

消息發送

默認的消息發送採用對消息隊列進行取模,肯定隊列。
其餘的方式好比輪訓方式等。

Kafka 有兩個默認的分配策略:

  • Range:該策略會把主題的若干個連續的分區分配給消費者。
  • RoundRobin:該策略把主題的全部分區逐個分配給消費者。

消費者

消費者向kafka訂閱topic,並從topic上接收消息。

消費者屬於消費者組,一個消費組的消費組訂閱的是同一個topic,每一個消費者接收topic一個partition的消息。

kafka默認的規則中,每一個分區只能被同一個消費組裏面的一個消費者消費。

1個消費者接收4個分區的消息:

2個消費者接收4個分區的消息:

4個消費者接收4個分區的消息:

5個消費者接收4個分區的消息:

若是消費者羣組的消費者超過主題的分區數量,那麼有一部分消費者就會被閒置,不會接收到任何消息。

兩個消費者羣組對應一個主題:

當一個消費者被關閉或發生崩潰時,它就離開羣組,本來由它讀取的分區將由羣組裏的其餘消費者來讀取。分區的全部權從一個消費者轉移到另外一個消費者,這樣的行爲被稱爲再均衡。在再均衡期間,消費者沒法讀取消息,形成整個羣組一小段時間的不可用。

經過上面消費者實例數量變化思考一個問題。在消費者機器重啓過程當中,存在partition和消費者從新創建聯繫的狀況,好比最開始有4個消費者,因爲並行重啓消費者,可能存在一段時間消費者數量變爲2個,當重啓完成後消費者數量有變成了4個。

這個過程存在消息可能重複發送到同一個消費者消費的狀況,形成重複消費,若是是對消息重複敏感的應用場景,我司自研的消息隊列組件會提供一個選項,消息在分區進行主動積壓,默認積壓30s等待消費者重啓完成,達到穩定的消費者數量。

消費者經過向被指派爲羣組協調器的 broker 發送心跳來維持它們和羣組的從屬關係以及它們對分區的全部權關係。消費者會在輪訓消息或提交偏移量時發送心跳。若是消費者中止發送心跳的時間足夠長,會話就會過時,羣組協調器認爲它已經死亡,就會觸發一次再均衡。

若是一個消費者發生崩潰,並中止讀取消息,羣組協調器會等待幾秒鐘,確認它死亡了纔會觸發再均衡。因此上面的延遲是因爲再平衡期間不可用形成的。

當消費者要加入羣組時,它會向羣組協調器發送一個 JoinGroup 請求。

第一個加入羣組的消費者將成爲"羣主"。羣主從協調器那裏得到羣組的成員列表,並負責給每個消費者分配分區。
分配完畢以後,羣主把分配狀況列表發送給羣組協調器,協調器再把這些信息發送給全部消費者。
每一個消費者只能看到本身的分配狀況。這個過程會在每次再均衡時重複發生。

消息消費

kafka消費者有本身消費偏移量,這個偏移量是從kafka中讀取的量,和kafka提交的偏移量不同。消費者通常須要第一次和rebalance的時候須要根據提交的偏移量來獲取數據,剩下的時候根據本身本地的偏移量來獲取。

當消費者使用了自動提交模式,當尚未提交的時候,有消費者加入或者移除,發送rebalance,再次消費時,消費者根據提交偏移量進行,可能產生重複消費數據。

選舉設計

先說分區leader的選舉,就是當ISR中的leader副本掛了,再從新選舉一個過程。

kafka中的選舉大體能夠分爲三大類:

  • 控制器選舉
  • 分區leader選舉
  • 消費組相關選舉

控制器選舉:
kafka集羣中有一個或多個broker,其中一個broker會被選舉爲kafka controller,負責管理整個集羣中全部分區和副本狀態。當檢測到某個分區的leader副本出現故障,controller負責爲該分區選舉新的leader副本。
若是檢測到某個分區ISR集合發生變化時,控制器負責通知全部的broker更新元數據信息。
kafka controller的實現是依賴於zk實現的,哪一個broker成功在zk的/controller臨時節點建立成功,就成爲kafka controller。

分區leader選舉:
在topic下增長分區或者分區下線時,都須要執行leader選舉。
基本思路是按照AR集合中副本順序查找第一個存活的副本,而且這個副本在ISR集合中。

消費者相關選舉:
消費組協調器須要爲消費組內的消費者選擇一個消費組leader,這個選舉算法比較簡單。
若是消費組內沒有leader,那麼第一個加入消費組的消費者成爲組leader。
若是因爲某種緣由leader消費者退出消費組,須要從新選舉leader,消費者協調器維護一個map結構,key爲消費組id,value爲消費者元信息,默認選擇第一個key做爲leader。

更多內容:

相關文章
相關標籤/搜索