大家好,我是樹哥。
訊息佇列可謂是高並行下的必備中介軟體了,而 Kafka 作為其中的佼佼者,經常被我們使用到各種各樣的場景下。隨著 Kafka 而來得,還有三個問題:訊息丟失、訊息重複、訊息順序。今天,樹哥帶大家聊聊訊息丟失的問題。
回到標題提出的問題:我們是否真的能保證 Kafka 訊息不丟失?
答案是:我們無法保證 Kafka 訊息不丟失,只能保證某種程度下,訊息不丟失。
這裡所說的某些情況,從嚴重程度依次為:Kafka 宕機、伺服器宕機、機房地震、城市毀滅、地球毀滅。不要覺得樹哥在危言聳聽,如果你的伺服器部署在烏克蘭的首都,那是不是就會遭遇城市毀滅的風險了?因此,我們根據業務的重要程度,設定合理的可靠性級別,畢竟可靠性級別越高,付出的成本越高。
如果你的應用是金融型別或者國民級別的應用,那麼你需要考慮機房地震以上級別的可靠性級別,否則一般考慮到伺服器宕機這個維度就可以了。對於機房地震級別以上的情況,大多數都是需要做異地多活,然後做好各地機房資料的實時同步。即使地球毀滅了,你在火星部署了一個機房,其原理也是類似。
我想大多數同學的應用可靠性,可能都只需要考慮到伺服器宕機級別,因此後續的考慮也僅限於這個級別。
要讓 Kafka 訊息不丟失,那麼我們必須知道 Kafka 可能在哪些地方丟資料,因此弄清楚 Kafka 訊息流轉的整個過程就非常重要了。對 Kafka 來說,其整體架構可以分為生產者、Kafka 伺服器、消費者三大塊,其整體架構如下圖所示。
對生產者來說,其傳送訊息到 Kafka 伺服器的過程可能會發生網路波動,導致訊息丟失。對於這一個可能存在的風險,我們可以通過合理設定 Kafka 使用者端的 request.required.acks
引數來避免訊息丟失。該參數列示生產者需要接收來自伺服器端的 ack 確認,當收不到確認或者超市時,便會丟擲異常,從而讓生產者可以進一步進行處理。
該引數可以設定不同級別的可靠性,從而滿足不同業務的需求,其引數設定及含義如下所示:
如上所示,如果業務對可靠性要求很高,那麼可以將 request.required.acks
引數設定為 -1,這樣就不會在生產者階段發生訊息丟失的問題。
當 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 訊息來說,只需要考慮如下幾個內容即可:
本文的思維導圖如下所示。
好了,這就是今天的分享了。
如果你喜歡今天的分享,記得一鍵三連支援我!你的鼓勵,是我寫文章最大的動力!