面試系列四 如何保證訊息的可靠性傳輸

2022-02-10 01:18:25 字數 3040 閱讀 6483

這個丟資料,mq一般分為兩種,要麼是mq自己弄丟了,要麼是我們消費的時候弄丟了。咱們從rabbitmq和kafka分別來分析一下吧

rabbitmq這種mq,一般來說都是承載公司的核心業務的,資料是絕對不能弄丟的

(1)rabbitmq

1)生產者弄丟了資料

生產者將資料傳送到rabbitmq的時候,可能資料就在半路給搞丟了,因為網路啥的問題,都有可能。

此時可以選擇用rabbitmq提供的事務功能,就是生產者傳送資料之前開啟rabbitmq事務(channel.txselect),然後傳送訊息,如果訊息沒有成功被rabbitmq接收到,那麼生產者會收到異常報錯,此時就可以回滾事務(channel.txrollback),然後重試傳送訊息;如果收到了訊息,那麼可以提交事務(channel.txcommit)。但是問題是,rabbitmq事務機制一搞,基本上吞吐量會下來,因為太耗效能。

所以一般來說,如果你要確保說寫rabbitmq的訊息別丟,可以開啟confirm模式,在生產者那裡設定開啟confirm模式之後,你每次寫的訊息都會分配乙個唯一的id,然後如果寫入了rabbitmq中,rabbitmq會給你回傳乙個ack訊息,告訴你說這個訊息ok了。如果rabbitmq沒能處理這個訊息,會**你乙個nack介面,告訴你這個訊息接收失敗,你可以重試。而且你可以結合這個機制自己在記憶體裡維護每個訊息id的狀態,如果超過一定時間還沒接收到這個訊息的**,那麼你可以重發。

事務機制和cnofirm機制最大的不同在於,事務機制是同步的,你提交乙個事務之後會阻塞在那兒,但是confirm機制是非同步的,你傳送個訊息之後就可以傳送下乙個訊息,然後那個訊息rabbitmq接收了之後會非同步**你乙個介面通知你這個訊息接收到了。

所以一般在生產者這塊避免資料丟失,都是用confirm機制的。

2)rabbitmq弄丟了資料

就是rabbitmq自己弄丟了資料,這個你必須開啟rabbitmq的持久化,就是訊息寫入之後會持久化到磁碟,哪怕是rabbitmq自己掛了,恢復之後會自動讀取之前儲存的資料,一般資料不會丟。除非極其罕見的是,rabbitmq還沒持久化,自己就掛了,可能導致少量資料會丟失的,但是這個概率較小。

設定持久化有兩個步驟,第乙個是建立queue的時候將其設定為持久化的,這樣就可以保證rabbitmq持久化queue的元資料,但是不會持久化queue裡的資料;第二個是傳送訊息的時候將訊息的deliverymode設定為2,就是將訊息設定為持久化的,此時rabbitmq就會將訊息持久化到磁碟上去。必須要同時設定這兩個持久化才行,rabbitmq哪怕是掛了,再次重啟,也會從磁碟上重啟恢復queue,恢復這個queue裡的資料。

而且持久化可以跟生產者那邊的confirm機制配合起來,只有訊息被持久化到磁碟之後,才會通知生產者ack了,所以哪怕是在持久化到磁碟之前,rabbitmq掛了,資料丟了,生產者收不到ack,你也是可以自己重發的。

哪怕是你給rabbitmq開啟了持久化機制,也有一種可能,就是這個訊息寫到了rabbitmq中,但是還沒來得及持久化到磁碟上,結果不巧,此時rabbitmq掛了,就會導致記憶體裡的一點點資料會丟失。

3)消費端弄丟了資料

rabbitmq如果丟失了資料,主要是因為你消費的時候,剛消費到,還沒處理,結果程序掛了,比如重啟了,那麼就尷尬了,rabbitmq認為你都消費了,這資料就丟了。

這個時候得用rabbitmq提供的ack機制,簡單來說,就是你關閉rabbitmq自動ack,可以通過乙個api來呼叫就行,然後每次你自己**裡確保處理完的時候,再程式裡ack一把。這樣的話,如果你還沒處理完,不就沒有ack?那rabbitmq就認為你還沒處理完,這個時候rabbitmq會把這個消費分配給別的consumer去處理,訊息是不會丟的。

(2)kafka

1)消費端弄丟了資料

唯一可能導致消費者弄丟資料的情況,就是說,你那個消費到了這個訊息,然後消費者那邊自動提交了offset,讓kafka以為你已經消費好了這個訊息,其實你剛準備處理這個訊息,你還沒處理,你自己就掛了,此時這條訊息就丟咯。

這不是一樣麼,大家都知道kafka會自動提交offset,那麼只要關閉自動提交offset,在處理完之後自己手動提交offset,就可以保證資料不會丟。但是此時確實還是會重複消費,比如你剛處理完,還沒提交offset,結果自己掛了,此時肯定會重複消費一次,自己保證冪等性就好了。

生產環境碰到的乙個問題,就是說我們的kafka消費者消費到了資料之後是寫到乙個記憶體的queue裡先緩衝一下,結果有的時候,你剛把訊息寫入記憶體queue,然後消費者會自動提交offset。

然後此時我們重啟了系統,就會導致記憶體queue裡還沒來得及處理的資料就丟失了

2)kafka弄丟了資料

這塊比較常見的乙個場景,就是kafka某個broker宕機,然後重新選舉partiton的leader時。大家想想,要是此時其他的follower剛好還有些資料沒有同步,結果此時leader掛了,然後選舉某個follower成leader之後,他不就少了一些資料?這就丟了一些資料啊。

生產環境也遇到過,我們也是,之前kafka的leader機器宕機了,將follower切換為leader之後,就會發現說這個資料就丟了

所以此時一般是要求起碼設定如下4個引數:

給這個topic設定replication.factor引數:這個值必須大於1,要求每個partition必須有至少2個副本

在kafka服務端設定min.insync.replicas引數:這個值必須大於1,這個是要求乙個leader至少感知到有至少乙個follower還跟自己保持聯絡,沒掉隊,這樣才能確保leader掛了還有乙個follower吧

在producer端設定acks=all:這個是要求每條資料,必須是寫入所有replica之後,才能認為是寫成功了

在producer端設定retries=max(很大很大很大的乙個值,無限次重試的意思):這個是要求一旦寫入失敗,就無限重試,卡在這裡了

我們生產環境就是按照上述要求配置的,這樣配置之後,至少在kafka broker端就可以保證在leader所在broker發生故障,進行leader切換時,資料不會丟失

3)生產者會不會弄丟資料

如果按照上述的思路設定了ack=all,一定不會丟,要求是,你的leader接收到訊息,所有的follower都同步到了訊息之後,才認為本次寫成功了。如果沒滿足這個條件,生產者會自動不斷的重試,重試無限次。

《RabbitMQ》如何保證訊息的可靠性

1.1 事務機制 amqp協議提供了事務機制,在投遞訊息時開啟事務支援,如果訊息投遞失敗,則回滾事務。自定義事務管理器 configuration public class rabbittranscation bean public rabbittemplate rabbittemplate con...

RabbitMQ如何保證訊息的可靠性投遞

目前來說,現在有兩種方案實施 1.資料庫持久化方案 2.訊息延遲投遞方案 資料庫持久化方案 流程 1.將業務訂單資料和生成的message進行持久化操作 一般情況下插入資料庫,這裡如果分庫的話可能涉及到分布式事務 2.將message傳送到broker伺服器中 3.通過rabbitmq的confir...

Kafka如何保證訊息的可靠性傳輸

1.消費端弄丟了資料 唯一可能導致消費者弄丟資料的情況,就是說,你消費到了這個訊息,然後消費者那邊自動提交了 offset,讓 kafka 以為你已經消費好了這個訊息,但其實你才剛準備處理這個訊息,你還沒處理,你自己就掛了,此時這條訊息就丟咯。這不是跟 rabbitmq 差不多嗎,大家都知道 kaf...