消息隊列如何保證消息能百分百成功被消費


消息隊列如何保證消息能百分百成功被消費

  目前常用的消息隊列有很多種,如RabbitMQ,ActiveMQ,Kafka...下面以RabbitMQ為例來講如何保證消息隊列中的信息能百分百被消費掉.

  其中消費隊列的工作流程如下:
  

  寫個偽代碼:  

public boolean sendOrderMessage(String orderMessage){
RabbitTemplate rb = new RabbitTemplate();

    try{
        rb.send(orderMessage);
    }catch (Exception e){
        return  false;
    }
    return  true;

}
  那么在訂單服務發送完消息的情況下,物流服務器突然宕機,或者MQ服務器突然宕機怎么辦?發送方已經將消息發送,而消費方卻沒有接收到任何需要消費的信息,這就會導致,消息消費失敗.

1)消息生產者把消息發送給MQ,如果接收成功,MQ會返回一個ack消息給生產者

2)如果消息接收不成功,MQ會返回一個nack消息給生產者

  碰到上面的問題,我們首先第一會想到解決方案就是發送方在消息發送到MQ之后,將消息給寫到硬盤上,這樣即使MQ服務器宕機,也不會導致未消費的消息丟失.

那么問題又出現了,當高並發場景下這種方式顯而易見是不合理的,因為與磁盤進行I/O交互是相對非常慢的.當然這種情況我們可以有解決方案: 可以在緩存中將消息緩存下來,比如當消息滿1000條時就將這些消息一次性寫到磁盤中.

這種方案雖然可以解決I/O交互問題.但是還是不能解決宕機出現的問題,假如緩存了900條消息,並且這些消息均為被消費,此時宕機了,同樣會導致消息的丟失.顯然這種解決方案還需要優化.

  在上面方案的基礎上.我們可以再增加一個機制,增加一個確認機制,如圖:

流程解釋:

1)訂單服務生產者再投遞消息之前,先把消息持久化到Redis或DB中,建議redis,高性能。消息的狀態為發送中。

2)confirm機制監聽消息是否發送成功?如ack成功消息,刪除redis中此消息。

3)如果nack不成功的消息,這個可以根據自身的業務選擇是否重發此消息。也可以刪除此消息,由自己的業務決定。

4)這邊加了個定時任務,來拉取隔一定時間了,消息狀態還是為發送中的,這個狀態就表明,訂單服務是沒有收到ack成功消息。

5)定時任務會作補償性的投遞消息。這個時候如果MQ回調ack成功接收了,再把redis中此消息刪除

這種方案其實就是加上一個補償機制,不管MQ有沒有真正的接收到,只要緩存redis中的消息還是發送中的狀態,就意味着這個消息沒有成功的投遞出去,也沒有被消費,定時任務啟動時就要重新發送.

當然定時任務那邊我們還可以加上一個補償的次數,如果大於3次,還是沒有收到ack消息,那就直接把消息的狀態設置為【失敗】,由人工去排查到底是為什么?

不過這樣的方案,就會有可能發送多次相同的消息,很有可能MQ已經收到了消息,就是ack消息回調時出現網絡故障,沒有讓生產者收到。那就要要求消費者一定在消費的時候保障冪等性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM