避免踩坑:如何防止Apache Kafka丟失資料? Kafka中那些為追求高可用性而忽視的高可靠性配置引數 - SoftwareMill技術部落格

banq發表於2020-04-02

討論Kafka中最重要的配置,從而能防止Kafka中的資料丟失。

生產者訊息確認機制

這是生產者級別的超重要配置。根據文件,訊息確認acks屬性是:

生產者客戶端要求kafka叢集中選舉的領導者在確認請求完成之前已收到的確認數。

可能的值是:0,1,all及-1(其等同於all)。

這是什麼意思?

如果將此值設定為“ 0”,則表示“發完訊息即忘記”。生產者客戶端只管悶頭繼續傳送訊息,無視kafka是否能夠儲存它傳送的訊息。當每個訊息都很重要時,這絕對不是一個好主意。

將其設定為“ 1”意味著kafka叢集能夠保留訊息時,生產者客戶端才繼續傳送訊息。聽起來好多了,但可以想象一下當Kafka叢集選舉的領導者伺服器節點崩潰了,並且尚未將訊息複製到叢集中其他備份節點時,這些訊息也會丟失。

屬性值“ all”表明它可以確保訊息在所有備份節點上都持久存在,但是事情更加複雜,實際上,這意味著它是在同步的所有備份節點上寫入操作。這令人困惑,並且可能是一個陷阱。稍後會更多。無論如何,“全部”(或“ -1”)是最安全的設定。

生產者重試機制

通過retries設定,可以微調在傳送失敗時,生產者應嘗試將訊息傳送給kafka多少次。預設值為2147483647,其為最大整數。

聽起來很簡單?好吧,就像在許多其他Kafka設定中一樣,這裡也是一個陷阱。實際上有兩個陷阱。

首先,還有另一個設定delivery.timeout.ms可以設定所有重試的超時時間。因此,如果retriesa是一個很大的數字,但超時很小,則訊息傳遞仍然會失敗。

其次,如果訊息的順序很重要,則將max.in.flight.requests.per.connection設定為1 至關重要。將其設定為大於1的值可能導致訊息重新排序。更多關於這在優秀文章,其解釋的細節問題

複製寫入

Kafka提供複製。在某個節點失敗的情況下,它可以成為“救命稻草”。有幾件重要的事情要記住:

  • 複製是在分割槽級別完成的,
  • 每個分割槽都有一個領導者和一個或多個關注者,
  • 追隨者從領導者那裡獲取資料,
  • 如果生產者acks設定為all—在同步所有副本備份節點上保留訊息後,生產者將被確認。

這些事實得出重要結論:

  • 如果您在多個站點(例如,多個可用性跨機房的區域)中的kafka中設定了高複製級別,則所有關注者獲取訊息都需要一些時間。
  • 如果領導者在收到訊息之後但在追隨者獲取此訊息之前失敗,則訊息將丟失。重播此訊息變成生產者客戶端的責任。這就是acks設定如此重要的原因。

可以使用以下命令配置所有主題的預設複製因子,default.replication.factor請注意,預設設定為1

建立主題時,也可以為每個主題設定複製因子。如果要為特定主題設定不同的設定,則最好將其設定auto.create.topics.enable為false並使用指令碼建立主題。

複製-特殊情況

複製還有2個其他設定:offsets.topic.replication.factor和transaction.state.log.replication.factor

這些是“特殊”主題的代理設定:第一個儲存的消費者客戶端offsets偏移量,而第二個儲存的是事務明細。請記住,特殊主題的設定不能使用常規主題的預設設定。

最小化同步副本

如前所述,生產者acks屬性確定Kafka叢集何時應確認該訊息,並且all設定更加安全。在這種情況下,“all”一詞是什麼意思?這意味著all in-sync replicas。

min.insync.replicas 意味:

當生產者將acks設定為“ all”(或“ -1”)時,min.insync.replicas指定必須確認寫入才能使成功寫入的最小備份副本數量。如果無法滿足此最小值,則生產者將引發異常(NotEnoughReplicas或NotEnoughReplicasAfterAppend)。

考慮場景:我們設定acks=all和min.insync.replicas=1(這是預設設定!)。網路不穩定,只有領導者處於同步狀態(例如,其他kafka節點失去了與Zookeeper的連線)。生產者將寫入訊息,並根據min.insync.replicas配置確認了。因為網路不穩定,領導者節點無法與其他備份節點正常通訊,這條訊息只儲存在領導者節點上,這意味著該訊息將永遠不會複製到其他備份節點並丟失。

這種情況似乎是不現實的,但這是一個真實的例子。

min.insync.replicas最小安全設定為2

預設值是1,會很危險,很容易忘記對其進行更改。

在min.insync.replicas上配置的代理將成為所有新的主題(你可以每個主題進行配置)的預設。

同樣,事務主題不使用此設定,它有自己的:transaction.state.log.min.isr。

不乾淨的領導人選舉

設定unclean.leader.election.enable為false,可以防止kafka叢集節點(如果不在ISR列表中)卻成為領導者。

為什麼?考慮一個場景:

  1. 您有3個kafka叢集節點,節點broker_1是一個領導者。
  2. 節點broker_3 由於某種原因離線。
  3. 節點broker_1從ISR列表中刪除它。
  4. 生產者繼續其工作,並寫一些訊息。
  5. 在broker_1和broker_2同時下線。
  6. 在broker_3復甦和再次線上,它成為了一個領導者。
  7. 在broker_2復甦和開始跟隨broker_3。

這是什麼意思?broker_3離線時,broker_1儲存和確認的訊息都會丟失。

消費者自動提交

當消費者客戶端收到一些訊息時,必須“告訴” Hello, I already got this message, please don't give it to me again if I ask for new messages!!,方法是提交偏移量。可以手動或自動完成。

如果將enable.auto.commit設定為true的偏移量,則會在後臺定期提交。因此,您無法控制何時傳送偏移,即使在處理訊息之前也可以傳送偏移。容易想象如果消費者失敗了會發生什麼,它不會獲得已經比確認的偏移量更“舊”的訊息,並且這些訊息會丟失。

(實際上,這有點複雜-偏移量儲存的不是針對特定的消費者例項,而是針對消費者組,但是現在讓我們簡化一下)。

如果您手動提交偏移量而使用者在處理訊息之後且提交偏移量之前失敗,會發生什麼情況?是的,該訊息被處理兩次,至多一次傳遞。如果唯一性很重要,則必須在處理過程中對訊息進行重複資料刪除,但這是另一個故事的主題。(banq注:收件箱模式)

訊息未同步到磁碟

當所有kafka經紀人節點都確認訊息後會發生什麼?這是否意味著它已經儲存在磁碟上了?其實沒有。

這隻意味著它儲存在經紀人的記憶體中。這是個問題嗎?是的,當所有經紀人同時失敗時,這可能是一個問題。所有經紀人都在同一個可用區中,這是最糟糕的做法。

通常,是作業系統決定將訊息重新整理到磁碟的時間,但是可以通過在代理級別設定log.flush.interval.messages 或 log.flush.interval.ms,或主題級別上設定flush.messages.flush.ms。

例如,設定flush.messages=1將導致將每條訊息寫入磁碟。您可以想象它會對效能產生重大影響,因此在進行操作之前請三思。

情況甚至更糟:如果將資料同步到磁碟,則可以將其儲存在磁碟快取中。但是幾乎不可能同時破壞所有代理中的所有磁碟的情況。另一方面,如果群集設計不良,並且所有代理都位於同一可用性區域中,則電源故障可能會導致資料丟失。

總結

Kafka是一個分散式系統,如果配置正確,可以超級持久。一些避免踩坑要點:

生產者設定

  • acks=all
  • retries=2147483647
  • delivery.timeout.ms=2147483647

經紀人設定

  • unclean.leader.election.enable=false
  • default.replication.factor=3
  • min.insync.replicas=2
  • offsets.topic.replication.factor=3
  • transaction.state.log.replication.factor=3
  • transaction.state.log.min.isr=2

消費者設定

  • enable.auto.commit=false

相關文章