伺服器當機會導致Kafka訊息丟失嗎

大雄45發表於2022-08-06
導讀 如果你的應用是金融型別或者國民級別的應用,那麼你需要考慮機房地震以上級別的可靠性級別,否則一般考慮到伺服器當機這個維度就可以了。對於機房地震級別以上的情況,大多數都是需要做異地多活,然後做好各地機房資料的實時同步。即使地球毀滅了,你在火星部署了一個機房,其原理也是類似。

訊息佇列可謂是高併發下的必備中介軟體了,而 Kafka 作為其中的佼佼者,經常被我們使用到各種各樣的場景下。隨著 Kafka 而來得,還有三個問題:訊息丟失、訊息重複、訊息順序。今天,樹哥帶大家聊聊訊息丟失的問題。

伺服器當機會導致Kafka訊息丟失嗎伺服器當機會導致Kafka訊息丟失嗎

可靠性級別

回到標題提出的問題:我們是否真的能保證 Kafka 訊息不丟失?

答案是:我們無法保證 Kafka 訊息不丟失,只能保證某種程度下,訊息不丟失。

這裡所說的某些情況,從嚴重程度依次為:Kafka 當機、伺服器當機、機房地震、城市毀滅、地球毀滅。不要覺得樹哥在危言聳聽,如果你的伺服器部署在烏克蘭的首都,那是不是就會遭遇城市毀滅的風險了?因此,我們根據業務的重要程度,設定合理的可靠性級別,畢竟可靠性級別越高,付出的成本越高。

如果你的應用是金融型別或者國民級別的應用,那麼你需要考慮機房地震以上級別的可靠性級別,否則一般考慮到伺服器當機這個維度就可以了。對於機房地震級別以上的情況,大多數都是需要做異地多活,然後做好各地機房資料的實時同步。即使地球毀滅了,你在火星部署了一個機房,其原理也是類似。

我想大多數同學的應用可靠性,可能都只需要考慮到伺服器當機級別,因此後續的考慮也僅限於這個級別。

從大局看 Kafka

要讓 Kafka 訊息不丟失,那麼我們必須知道 Kafka 可能在哪些地方丟資料,因此弄清楚 Kafka 訊息流轉的整個過程就非常重要了。對 Kafka 來說,其整體架構可以分為生產者、Kafka 伺服器、消費者三大塊,其整體架構如下圖所示。

伺服器當機會導致Kafka訊息丟失嗎伺服器當機會導致Kafka訊息丟失嗎

生產者

對生產者來說,其傳送訊息到 Kafka 伺服器的過程可能會發生網路波動,導致訊息丟失。對於這一個可能存在的風險,我們可以透過合理設定 Kafka 客戶端的 request.required.acks 引數來避免訊息丟失。該參數列示生產者需要接收來自服務端的 ack 確認,當收不到確認或者超市時,便會丟擲異常,從而讓生產者可以進一步進行處理。

該引數可以設定不同級別的可靠性,從而滿足不同業務的需求,其引數設定及含義如下所示:

  • request.required.acks = 0表示 Producer 不等待來自 Leader 的 ACK 確認,直接傳送下一條訊息。在這種情況下,如果 Leader 分片所在伺服器發生當機,那麼這些已經傳送的資料會丟失。
  • request.required.acks = 1表示 Producer 等待來自 Leader 的 ACK 確認,當收到確認後才傳送下一條訊息。在這種情況下,訊息一定會被寫入到 Leader 伺服器,但並不保證 Follow 節點已經同步完成。所以如果在訊息已經被寫入 Leader 分片,但是還未同步到 Follower 節點,此時 Leader 分片所在伺服器當機了,那麼這條訊息也就丟失了,無法被消費到。
  • request.required.acks = -1表示 Producer 等待來自 Leader 和所有 Follower 的 ACK 確認之後,才傳送下一條訊息。在這種情況下,除非 Leader 節點和所有 Follower 節點都當機了,否則不會發生訊息的丟失。
  • 如上所示,如果業務對可靠性要求很高,那麼可以將 request.required.acks 引數設定為 -1,這樣就不會在生產者階段發生訊息丟失的問題。

    Kafka 伺服器

    當 Kafka 伺服器接收到訊息後,其並不直接寫入磁碟,而是先寫入記憶體中。隨後,Kafka 服務端會根據不同設定引數,選擇不同的刷盤過程,這裡有兩個引數控制著這個刷盤過程:

# 資料達到多少條就將訊息刷到磁碟
#log.flush.interval.messages=10000
# 多久將累積的訊息刷到磁碟,任何一個達到指定值就觸發寫入
#log.flush.interval.ms=1000

如果我們設定 log.flush.interval.messages=1,那麼每次來一條訊息,就會刷一次磁碟。透過這種方式,就可以降低訊息丟失的機率,這種情況我們稱之為同步刷盤。 反之,我們稱之為非同步刷盤。與此同時,Kafka 伺服器也會進行副本的複製,該 Partition 的 Follower 會從 Leader 節點拉取資料進行儲存。然後將資料儲存到 Partition 的 Follower 節點中。

對於 Kafka 服務端來說,其會根據生產者所設定的 request.required.acks 引數,選擇什麼時候回覆 ack 給生產者。對於 acks 為 0 的情況,表示不等待 Kafka 服務端 Leader 節點的確認。對於 acks 為 1 的情況,表示等待 Kafka 服務端 Leader 節點的確認。對於 acks 為 1 的情況,表示等待 Kafka 服務端 Leader 節點好 Follow 節點的確認。

但要注意的是,Kafka 服務端返回確認之後,僅僅表示該訊息已經寫入到 Kafka 伺服器的 PageCache 中,並不代表其已經寫入磁碟了。這時候如果 Kafka 所在伺服器斷電或當機,那麼訊息也是丟失了。而如果只是 Kafka 服務崩潰,那麼訊息並不會丟失。

因此,對於 Kafka 服務端來說,即使你設定了每次刷 1 條訊息,也是有可能發生訊息丟失的,只是訊息丟失的機率大大降低了。

消費者

對於消費者來說,如果其拉取訊息之後自動返回 ack,但消費者服務在處理過程中發生崩潰退出,此時這條訊息就相當於丟失了。對於這種情況,一般我們都是採用業務處理完之後,手動提交 ack 的方式來避免訊息丟失。

在我們在業務處理完提交 ack 這種情況下,有可能發生訊息重複處理的情況,即業務邏輯處理完了,但在提交 ack 的時候發生異常。這要求消費者在處理業務的時候,每一處都需要進行冪等處理,避免重複處理業務。

能不丟失嗎?

根據我們上面的分析,Kafka 只能做到 Kafka 應用崩潰這個級別,因為 Kafka 的 acks 僅僅表示寫入了 PageCache。

如果伺服器當機了,即使我們設定了每來一條訊息就寫入一次磁碟,那麼也有可能在寫入 PageCache 後、寫入磁碟前這個關鍵點,伺服器發生當機。這時候 PageCache 裡面的訊息資料就沒了,那麼訊息自然也就丟失了。但如果僅僅是 Kafka 應用崩潰退出,因為其已經寫入到 PageCache 中了,那麼系統自然會將其寫入到磁碟中,因此訊息並不會丟失。

總結

訊息可靠性級別,一定是跟業務重要性關聯在一起的。我們無法拋開業務本身的重要性來談可靠性,只能是取一個平衡的值。

根據我的經驗來說,除非是金融類或國民級別的應用,否則只需要考慮到伺服器當機的級別就可以了。而如果是金融級別或國民級別的應用,那麼就需要考慮到城市毀滅的可靠性級別。但地球毀滅這個,我想誰也不會去考慮吧。

對於大多數的應用,考慮伺服器當機級別的情況下,對於 Kafka 訊息來說,只需要考慮如下幾個內容即可:

生產者。根據業務重要性,設定好 acks 引數,並做好業務重試,以及告警記錄即可。

Kafka 服務端。根據業務重要性,設定好刷盤引數即可,一般來說都不需要設定成同步刷盤。

消費者。使用手動提交 acks 的方式,避免丟失訊息,同時需要做好冪等處理,避免重複處理。

本文的思維導圖如下所示。

伺服器當機會導致Kafka訊息丟失嗎伺服器當機會導致Kafka訊息丟失嗎

原文來自:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2909303/,如需轉載,請註明出處,否則將追究法律責任。

相關文章