Kafka技術知識總結之五 Kafka的高可用性

2021-10-07 21:11:53 字數 3869 閱讀 8078

接上篇《kafka技術知識總結之四——kafka 再均衡》

kafka 實現高可用性的方式是進行 replication。對於 kafka,如果沒有提供高可用性機制,一旦乙個或多個 broker 宕機,則宕機期間其上所有 partition 都無法繼續提供服務。若該 broker永遠不能再恢復,那麼所有的資料也就將丟失,這是不可容忍的。所以 kafka 高可用性的設計也是進行 replication。

replica 的分布:為了盡量做好負載均衡和容錯能力,需要將同乙個 partition 的 replica 盡量分散到不同的機器。

replica 的同步:當有很多 replica 的時候,一般來說,對於這種情況有兩個處理方法:

而 kafka 選取了乙個折中的方式:isr (in-sync replicas)。producer 每次傳送訊息,將訊息傳送給 leader,leader 將訊息同步給他「信任」的「小弟們」就算成功,巧妙的均衡了確保資料不丟失以及吞吐率。具體步驟:

在所有的 replica 中,leader 會維護乙個與其基本保持同步的 replica 列表,該列表稱為isr (in-sync replica);每個 partition 都會有乙個 isr,而且是由 leader 動態維護。

如果乙個 replica 落後 leader 太多,leader 會將其剔除。如果另外的 replica 跟上腳步,leader 會將其加入。

同步:leader 向 isr 中的所有 replica 同步訊息,當收到所有 isr 中 replica 的 ack 之後,leader 才 commit。

非同步:收到同步訊息的 isr 中的 replica,非同步將訊息同步給 isr 集合外的 replica。

《訊息佇列面試題要點》

上述問題換一種問法,可以翻譯為**如何保證訊息佇列的冪等性?**這個問題可以認為是訊息佇列領域的基本問題。這個問題的回答可以根據具體的業務場景來答,沒有固定的答案。

無論是哪種訊息佇列,造成重複消費原因其實都是類似的。正常情況下,消費者在消費訊息的時候,消費完畢後,會傳送乙個確認訊息給訊息佇列,訊息佇列就知道該訊息被消費了,就會將該訊息從訊息佇列中刪除。只是不同的訊息佇列發出的確認訊息形式不同(例如 rabbitmq 是傳送乙個 ack 確認訊息,rocketmq 是返回乙個 consume_success 成功標誌),kafka 是通過**提交 offset 的方式**讓訊息佇列知道自己已經消費過了。

造成重複消費的原因,就是因為網路傳輸等等故障,確認資訊沒有傳送到訊息佇列,導致訊息佇列不知道自己已經消費過該訊息了,再次將訊息分發給其他的消費者。

如何解決?這個問題針對業務場景來答,分以下三種情況:

資料庫主鍵:拿到這個訊息做資料庫的 insert 操作,那就容易了,給這個訊息做乙個唯一的主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。

redis set 操作:拿到這個訊息做 redis 的 set 的操作,那就容易了,不用解決,set 操作無論幾次結果都是一樣的,因為 set 操作本來就是冪等操作。

kafka 有三種訊息投遞語義:

整體的訊息投遞語義由生產者、消費者兩端同時保證。

producer 端保證訊息投遞重複性,是通過producer 的 acks 引數broker 端的 min.insync.replicas 引數決定的。

producer 端的acks 引數值資訊如下:

前面 producer 的 acks = 1 可以保證寫入 leader 副本,對大部分情況沒有問題。但如果剛剛一條訊息寫入 leader,還沒有把這條訊息同步給其他 replica,leader 就掛了,那麼這條訊息也就丟失了。所以如果保證訊息的完全投遞,還是需要令 acks = all;

首先上面說到,為了配合 producer acks 引數為 all,需要令 broker 端引數 min.insync.replicas = 2;

min.insync.replicas 引數是用來配合 producer acks 引數的。因為如果 acks 設定為 all,但某個 topic 只有 leader 乙個 replica(或者某個 kafka 集群中由於同步很慢,導致所有 follower 全部被剔除 isr 集合),這樣 acks = -1 就演變成了 acks = 1。

所以需要 broker 端設定 min.insync.replicas 引數:當引數值為 2 時,如果副本數小於 2 個,會丟擲異常。

注:然而在筆者的使用環境中,訂閱是 kafka 主要的使用場景之一,方式是對於想要訂閱的某個 topic,每個使用者建立並獨享乙個不會重複的消費組。所以這樣的情況下,環境下的 min.insync.replicas 只能等於 1;

除此之外,broker 端還有乙個需要注意的引數unclean.leader.election.enable。該引數為 true 的時候,表示在 leader 下線的時候,可以從非 isr 集合中選舉出新的 leader。這樣的話可能會造成資料的丟失。所以如果需要在 broker 端的 unclean.leader.election.enable 設定為 false。

consumer 端比較麻煩,原因是需要考慮到某個 consumer 宕機後,同 consumer group 會發生負載均衡,同 group 其他的 consumer 會重新接管並繼續消費。

假設兩種場景:

第乙個場景,consumer 先提交 offset,再處理訊息。**如下:

list

messages = consumer.

poll()

;consumer.

commitoffset()

;processmsg

(messages)

;

這種情形下,提交 offset 成功,但處理訊息失敗,同時當前 consumer 宕機,這時候發生負載均衡,其他 consumer 從已經提交的 offset之後繼續消費。這樣的情況保證了at most once的消費語義,當然也可能會丟訊息。

第二個場景,consumer 先處理訊息,再提交 offset。**如下:

list

messages = consumer.

poll()

;processmsg

(messages)

;consumer.

commitoffset()

;

這種情形下,訊息處理成功,提交 offset 失敗,同時當前 consumer 宕機,這時候發生負載均衡,其他 consumer 依舊從同樣的 offset 拉取訊息消費。這樣的情況保證了at least once的消費語義,可能會重複消費訊息。

上述機制的保證都不是直接乙個配置可以解決的,而是 consumer 端**的處理先後順序問題完成的。

注:關於 kafka解耦作用的思考:

註冊中心可以將服務於服務之間解耦,但 kafka 也可以通過相同的 topic 的訊息傳遞實現業務的解耦。這兩種形式都可以實現解耦,但筆者個人理解:

java基礎技術知識點總結 JDBC

jdbc主要是 載入驅動 class.forname com.mysql.jdbc.driver 連線到指定資料庫,需要制定url username password,可以直接寫也可以用配置檔案等 connection conn drivermanager.getconnection jdbc my...

Spring技術知識點總結之一 迴圈依賴

spring ioc迴圈依賴解決方案分析 spring5原始碼閱讀 如何解決迴圈依賴?在 spring 中獲取乙個 bean,是通過獲取 beandefinition 實現的 在定義 bean 資訊的 xml 檔案中,beandefinitionreader 讀取指定路徑下的 xml 檔案,獲取 b...

WebRtc(網頁即時通訊技術)知識點總結

前言實現原理 p2p連線模式 一般我們傳統的連線方式,都是以伺服器為中介的模式 類似http協議 客戶端服務端 當然這裡服務端返回的箭頭僅僅代表返回請求資料 而點對點的連線恰恰資料通道一旦形成,中間是不經過服務端的,資料直接從乙個客戶端流向另乙個客戶端 客戶端a 客戶端b 客戶端a 客戶端c 可以無...