阿里二面:要保證訊息不丟失,又不重複,訊息佇列怎麼選型?
來源:君哥聊技術
大家好,我是君哥。
在使用訊息佇列時,有兩個經常讓我們煩惱的問題,訊息丟失和訊息重複。那我們在做技術選型時,有沒有一個訊息佇列能解決訊息丟失和訊息重複這兩個問題呢?
訊息丟失
如上圖,從生產者傳送訊息,Broker 儲存訊息,消費者消費訊息,每一個環節都有可能丟失訊息。
傳送丟失
生產者傳送訊息時,如果處理不當,很可能會造成訊息丟失。
生產者傳送訊息,主流訊息佇列都支援同步傳送和非同步傳送。如果使用同步傳送,生產者傳送訊息後,會同步等待 Broker 返回的 ACK,收到 ACK 訊息,就認為訊息傳送成功。如果長時間沒有收到,則會認為訊息傳送失敗,需要進行重試。
同步傳送可以保證訊息不丟失,但是會有效能問題,所以多數情況會選擇非同步傳送。非同步傳送如何保證訊息不丟失呢?主流訊息佇列(比如 Kafka 和 RocketMQ)實現方法基本類似,使用回撥函式來實現。下面看一下 Kafka 的非同步傳送程式碼:
producer.send(record, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
logger.error("傳送訊息失敗:", exception);
}
if (metadata != null) {
logger.info("訊息傳送成功");
}
}
});
訊息儲存
生產者傳送訊息成功,也不能保證訊息絕對不丟失。因為即使訊息傳送到 Broker,如果在消費者拉取到訊息之前,Broker 當機了,訊息還沒有落盤,也會導致訊息丟失。
在儲存階段要保證訊息不丟失,可以考慮幾個方面:
同步刷盤
採用非同步刷盤,如果在訊息落盤之前 Broker 當機了,就會造成訊息丟失。而採用同步刷盤,等待訊息落盤之後,再給 Sender 返回傳送成功,可以從訊息傳送環節保證訊息不丟失。
在 RocketMQ 中,把 flushDiskType 引數配置為 SYNC_FLUSH 就可以開啟同步刷盤。
Broker 叢集
如果 Broker 叢集中只有一個節點,即使訊息落盤成功了,Broker 傳送故障,在 Broker 恢復以前消費者也會拉取不到訊息。而且如果 Broker 磁碟故障不可恢復,訊息也會丟失。
採用 Broker 叢集可以很好地解決這個問題。見下圖:
在 Broker 叢集時,可以等待 2 個以上的節點同步訊息完成後再給 Producer 返回成功。這樣即使一個 Broker 掛了,也可以很容易找到替代的 Broker。
訊息消費
消費者保證不丟失訊息,需要消費完成後再給 Broker 返回 ACK。在主流的訊息佇列中,如果 Broker 收不到 ACK,都會給消費者再次傳送這條訊息。
有時候為了解決訊息積壓的問題,消費者拉取到訊息後會直接返回 ACK,然後再非同步執行訊息處理邏輯。這樣要保證訊息不丟失,需要在返回 ACK 之前把訊息儲存到本地,比如持久化到資料庫,後面可以取資料庫儲存的訊息進行處理。
訊息重複
訊息重複一般有兩個原因,一個是生產者傳送訊息後沒有收到 ACK,然後進行重複傳送,另一個原因是消費者消費完成後 Broker 沒有收到 ACK,導致訊息重複推送給消費者。
重複訊息會對業務造成影響,比如電商場景中的重複支付、賬務場景中的重複記賬,對業務造成的影響都比較嚴重。
從目前主流的訊息佇列來看,並沒有一個訊息佇列能解決訊息重複消費的問題,只能在消費端做冪等處理。下面提供幾個思路作為參考。
資料庫唯一鍵約束
如果訊息會落本地資料庫,可以採用訊息 ID 作為唯一鍵。如果訊息不落資料庫,可以將訊息 ID 或者訊息中其他唯一能標識訊息的屬性作為唯一鍵落業務資料表。
儲存消費記錄
我們也可以將訊息 ID 儲存 Redis,消費訊息前判斷訊息 ID 是否已存在。
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
Boolean result = valueOperations.setIfAbsent(messageId, messageId);
if (result) {
//消費邏輯;
} else {
logger.error("這條訊息已經消費,跳過,訊息ID:{}", messageId);
}
這裡有一個注意點,如果消費失敗了,需要刪除 Redis 中儲存的訊息 ID。
總結
訊息不丟失、不重複是訊息佇列的基本要求,但這個基本要求還是很難滿足的。
訊息丟失這個要求,主流訊息佇列透過訊息重試和訊息持久化的方式可以滿足。
但訊息重試也同時帶來了訊息重複的可能性,主流訊息佇列在解決重複訊息的問題上並沒有現成的方案,對不允許重複消費的場景,需要開發人員在消費端做冪等處理。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024924/viewspace-3003964/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- RabbitMQ-如何保證訊息不丟失MQ
- Kafka如何保證訊息不丟之無訊息丟失配置Kafka
- RabbitMq如何確保訊息不丟失MQ
- 分散式訊息佇列:如何保證訊息不被重複消費?(訊息佇列消費的冪等性)分散式佇列
- 二、如何保證訊息佇列的高可用?佇列
- 訊息佇列-如何保證訊息的不被重複消費(如何保證訊息消費的冪等性)佇列
- Redis 使用 List 實現訊息佇列能保證訊息可靠麼?Redis佇列
- 訊息佇列之如何保證訊息的可靠傳輸佇列
- 分散式訊息佇列:如何保證訊息的順序性分散式佇列
- 面試題剖析,如何保證訊息佇列的高可用?面試題佇列
- 如何保證訊息佇列的順序性?佇列
- 關於MQ的幾件小事(四)如何保證訊息不丟失MQ
- mq要如何處理訊息丟失、重複消費?MQ
- IoT裝置訊息洪峰怎麼扛? 阿里雲AIoT訊息佇列深度解讀阿里AI佇列
- 訊息佇列系列一:訊息佇列應用佇列
- Java面試—訊息佇列Java面試佇列
- RocketMQ的訊息是怎麼丟失的MQ
- RabbitMQ使用教程(四)如何通過持久化保證訊息99.99%不丟失?MQ持久化
- 訊息佇列佇列
- win10 訊息佇列服務怎麼開啟_win10怎麼新增訊息佇列Win10佇列
- 阿里雲訊息佇列 Kafka-訊息檢索實踐阿里佇列Kafka
- 阿里Java面試題剖析:為什麼使用訊息佇列?訊息佇列有什麼優點和缺點?阿里Java面試題佇列
- 如何保證訊息佇列的可靠性傳輸?佇列
- RabbitMQ訊息佇列(二):”Hello, World“MQ佇列
- 什麼是訊息佇列?佇列
- RabbitMQ防止訊息丟失MQ
- 高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?
- 金融行業訊息佇列選型及實踐行業佇列
- TimeLine模型下確保訊息有序不丟模型
- RabbitMQ訊息佇列(五):Routing 訊息路由MQ佇列路由
- 訊息機制篇——初識訊息與訊息佇列佇列
- 訊息推送平臺有沒有保證資料不丟?
- kafka 訊息佇列Kafka佇列
- 訊息佇列(MQ)佇列MQ
- [Redis]訊息佇列Redis佇列
- [訊息佇列]rocketMQ佇列MQ
- [訊息佇列]RabbitMQ佇列MQ
- Kafka訊息佇列Kafka佇列