如何保證訊息的可靠性傳輸?或者說,如何處理訊息丟失的問題?
如果說你這個是用 mq 來傳遞非常核心的訊息,比如說計費、扣費的一些訊息,那必須確保這個 mq 傳遞過程中絕對不會把計費訊息給弄丟。
訊息中介軟體各種面試題:
訊息中介軟體面試題:訊息丟失怎麼辦?
訊息中介軟體面試題:訊息佇列的優缺點,區別
訊息中介軟體面試題:訊息中介軟體的高可用
訊息中介軟體面試題:如何保證訊息的順序性
訊息中介軟體面試題:如何保證訊息不被重複消費
訊息中介軟體面試題:如何解決訊息佇列的延時以及過期失效問題?訊息佇列滿了以後該怎麼處理?有幾百萬訊息持續積壓幾小時呢?
訊息中介軟體面試題:如果讓你寫乙個訊息佇列,該如何進行架構設計?
面試題剖析
資料的丟失問題,可能出現在生產者、mq、消費者中,咱們從 rabbitmq 和 kafka 分別來分析一下吧。
生產者弄丟了資料
生產者將資料傳送到 rabbitmq 的時候,可能資料就在半路給搞丟了,因為網路問題啥的,都有可能。
此時可以選擇用 rabbitmq 提供的事務功能,就是生產者傳送資料之前開啟 rabbitmq 事務channel.txselect,然後傳送訊息,如果訊息沒有成功被 rabbitmq 接收到,那麼生產者會收到異常報錯,此時就可以回滾事務channel.txrollback,然後重試傳送訊息;如果收到了訊息,那麼可以提交事務channel.txcommit。
// 開啟事務
channel.txselect
try catch (exception e)
// 提交事務
channel.txcommit
但是問題是,rabbitmq 事務機制(同步)一搞,基本上吞吐量會下來,因為太耗效能。
所以一般來說,如果你要確保說寫 rabbitmq 的訊息別丟,可以開啟 confirm 模式,在生產者那裡設定開啟 confirm 模式之後,你每次寫的訊息都會分配乙個唯一的 id,然後如果寫入了 rabbitmq 中,rabbitmq 會給你回傳乙個 ack 訊息,告訴你說這個訊息 ok 了。如果 rabbitmq 沒能處理這個訊息,會**你的乙個 nack 介面,告訴你這個訊息接收失敗,你可以重試。而且你可以結合這個機制自己在記憶體裡維護每個訊息 id 的狀態,如果超過一定時間還沒接收到這個訊息的**,那麼你可以重發。
事務機制和 cnofirm 機制最大的不同在於,事務機制是同步的,你提交乙個事務之後會阻塞在那兒,但是 confirm 機制是非同步的,你傳送個訊息之後就可以傳送下乙個訊息,然後那個訊息 rabbitmq 接收了之後會非同步**你的乙個介面通知你這個訊息接收到了。
所以一般在生產者這塊避免資料丟失,都是用 confirm 機制的。
rabbitmq 弄丟了資料
就是 rabbitmq 自己弄丟了資料,這個你必須開啟 rabbitmq 的持久化,就是訊息寫入之後會持久化到磁碟,哪怕是 rabbitmq 自己掛了,恢復之後會自動讀取之前儲存的資料,一般資料不會丟。除非極其罕見的是,rabbitmq 還沒持久化,自己就掛了,可能導致少量資料丟失,但是這個概率較小。
設定持久化有兩個步驟:
建立 queue 的時候將其設定為持久化
這樣就可以保證 rabbitmq 持久化 queue 的元資料,但是它是不會持久化 queue 裡的資料的。
第二個是傳送訊息的時候將訊息的 deliverymode 設定為 2
就是將訊息設定為持久化的,此時 rabbitmq 就會將訊息持久化到磁碟上去。
必須要同時設定這兩個持久化才行,rabbitmq 哪怕是掛了,再次重啟,也會從磁碟上重啟恢復 queue,恢復這個 queue 裡的資料。
注意,哪怕是你給 rabbitmq 開啟了持久化機制,也有一種可能,就是這個訊息寫到了 rabbitmq 中,但是還沒來得及持久化到磁碟上,結果不巧,此時 rabbitmq 掛了,就會導致記憶體裡的一點點資料丟失。
所以,持久化可以跟生產者那邊的 confirm 機制配合起來,只有訊息被持久化到磁碟之後,才會通知生產者 ack 了,所以哪怕是在持久化到磁碟之前,rabbitmq 掛了,資料丟了,生產者收不到 ack,你也是可以自己重發的。
消費端弄丟了資料
rabbitmq 如果丟失了資料,主要是因為你消費的時候,剛消費到,還沒處理,結果程序掛了,比如重啟了,那麼就尷尬了,rabbitmq 認為你都消費了,這資料就丟了。
這個時候得用 rabbitmq 提供的 ack 機制,簡單來說,就是你必須關閉 rabbitmq 的自動 ack,可以通過乙個 api 來呼叫就行,然後每次你自己**裡確保處理完的時候,再在程式裡 ack 一把。這樣的話,如果你還沒處理完,不就沒有 ack 了?那 rabbitmq 就認為你還沒處理完,這個時候 rabbitmq 會把這個消費分配給別的 consumer 去處理,訊息是不會丟的。
消費端弄丟了資料
唯一可能導致消費者弄丟資料的情況,就是說,你消費到了這個訊息,然後消費者那邊自動提交了 offset,讓 kafka 以為你已經消費好了這個訊息,但其實你才剛準備處理這個訊息,你還沒處理,你自己就掛了,此時這條訊息就丟咯。
這不是跟 rabbitmq 差不多嗎,大家都知道 kafka 會自動提交 offset,那麼只要關閉自動提交 offset,在處理完之後自己手動提交 offset,就可以保證資料不會丟。但是此時確實還是可能會有重複消費,比如你剛處理完,還沒提交 offset,結果自己掛了,此時肯定會重複消費一次,自己保證冪等性就好了。
生產環境碰到的乙個問題,就是說我們的 kafka 消費者消費到了資料之後是寫到乙個記憶體的 queue 裡先緩衝一下,結果有的時候,你剛把訊息寫入記憶體 queue,然後消費者會自動提交 offset。然後此時我們重啟了系統,就會導致記憶體 queue 裡還沒來得及處理的資料就丟失了。
kafka 弄丟了資料
這塊比較常見的乙個場景,就是 kafka 某個 broker 宕機,然後重新選舉 partition 的 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 切換時,資料不會丟失。
生產者會不會弄丟資料?
如果按照上述的思路設定了 acks=all,一定不會丟,要求是,你的 leader 接收到訊息,所有的 follower 都同步到了訊息之後,才認為本次寫成功了。如果沒滿足這個條件,生產者會自動不斷的重試,重試無限次。
訊息中介軟體
1.訊息的優先順序 2.訊息排序 3.訊息過濾 4.訊息持久化 5.訊息重試 6.事務的支援 7.broker滿 生產者,佇列,消費者 訊息佇列的優點 1 解耦2 非同步訊息,系統響應 在jms中,有兩種訊息模型 點對點模式和發布訂閱模式。1.在點對點模式中 有三種角色 1 訊息佇列,傳送者,接受者...
訊息中介軟體
如何理解訊息中介軟體?訊息中介軟體是儲存訊息的乙個容器,與資料庫不同的是資料庫儲存的資料是可以被修改的,而訊息中介軟體一般不會被修改 訊息中介軟體在消費的生產者與消費者產生,相當於乙個中間人的角色,提供了路由保證訊息的傳遞,如果消費者不能及時接收,訊息會保留下來,知道消費者上線 保證在存活期內 訊息...
訊息中介軟體
訊息中介軟體是在訊息的傳輸過程中儲存訊息 訊息傳遞過程中不能更改 的容器。訊息中介軟體再將訊息從它的原中繼到它的目標時充當中間人的作用。訊息中介軟體的主要目的是提供路由並保證訊息的傳遞 如果傳送訊息時接收者不可用,訊息佇列會保留訊息,知道可以成功傳遞為止,當然,訊息佇列儲存訊息也是有期限的。訊息傳送...