為了防止消息重復消費導致業務處理異常,云消息隊列 RocketMQ 版的消費者在接收到消息后,有必要根據業務上的唯一Key對消息做冪等處理。本文介紹消息冪等的概念、適用場景以及處理方法。
什么是消息冪等
當出現消費者對某條消息重復消費的情況時,重復消費的結果與消費一次的結果是相同的,并且多次消費并未對業務系統產生任何負面影響,那么這個消費者的處理過程就是冪等的。
例如,在支付場景下,消費者消費扣款消息,對一筆訂單執行扣款操作,扣款金額為100元。如果因網絡不穩定等原因導致扣款消息重復投遞,消費者重復消費了該扣款消息,但最終的業務結果是只扣款一次,扣費100元,且用戶的扣款記錄中對應的訂單只有一條扣款流水,不會多次扣除費用。那么這次扣款操作是符合要求的,整個消費過程實現了消息冪等。
適用場景
在互聯網應用中,尤其在網絡不穩定的情況下,云消息隊列 RocketMQ 版的消息有可能會出現重復。如果消息重復會影響您的業務處理,請對消息做冪等處理。
消息重復的場景如下:
發送時消息重復
當一條消息已被成功發送到服務端并完成持久化,此時出現了網絡閃斷或者客戶端宕機,導致服務端對客戶端應答失敗。 如果此時生產者意識到消息發送失敗并嘗試再次發送消息,消費者后續會收到兩條內容相同但Message ID不同的消息。
投遞時消息重復
消息消費的場景下,消息已投遞到消費者并完成業務處理,當客戶端給服務端反饋應答的時候網絡閃斷。為了保證消息至少被消費一次,云消息隊列 RocketMQ 版的服務端將在網絡恢復后再次嘗試投遞之前已被處理過的消息,消費者后續會收到兩條內容相同并且Message ID也相同的消息。
負載均衡時消息重復(包括但不限于網絡抖動、Broker重啟以及消費者應用重啟)
當云消息隊列 RocketMQ 版的Broker或客戶端重啟、擴容或縮容時,會觸發Rebalance,此時消費者可能會收到少量重復消息。
處理方法
因為不同的Message ID對應的消息內容可能相同,有可能出現沖突(重復)的情況,所以真正安全的冪等處理,不建議以Message ID作為處理依據。最好的方式是以業務唯一標識作為冪等處理的關鍵依據,而業務的唯一標識可以通過消息Key設置。
以支付場景為例,可以將消息的Key設置為訂單號,作為冪等處理的依據。具體代碼示例如下:
Message message = new Message();
message.setKey("ORDERID_100");
SendResult sendResult = producer.send(message);
消費者收到消息時可以根據消息的Key,即訂單號來實現消息冪等:
consumer.subscribe("ons_test", "*", new MessageListener() {
public Action consume(Message message, ConsumeContext context) {
String key = message.getKey()
// 根據業務唯一標識的Key做冪等處理。
}
});