RabbitMQ消息可靠投遞及消費機制

Consumer Acknowledgements機制

當consumer消費端成功消費完消息後,返回給broker確認通知,告訴broker移除隊列中已經消費成功的消息,如果消費端消費失敗,可以通知broker將消費失敗的消息重新放回隊列中,以便繼續消費。

  1. channel.basicAck(deliveryTag, multiple);

consumer處理成功後,通知broker刪除隊列中的消息,如果設置multiple=true,表示支持批量確認機制以減少網絡流量。

例如:有值爲5,6,7,8 deliveryTag的投遞

如果此時channel.basicAck(8, true);則表示前面未確認的5,6,7投遞也一起確認處理完畢。

如果此時channel.basicAck(8, false);則僅表示deliveryTag=8的消息已經成功處理。

  1. channel.basicNack(deliveryTag, multiple, requeue);

consumer處理失敗後,例如:有值爲5,6,7,8 deliveryTag的投遞。

如果channel.basicNack(8, true, true);表示deliveryTag=8之前未確認的消息都處理失敗且將這些消息重新放回隊列中。

如果channel.basicNack(8, true, false);表示deliveryTag=8之前未確認的消息都處理失敗且將這些消息直接丟棄。

如果channel.basicNack(8, false, true);表示deliveryTag=8的消息處理失敗且將該消息重新放回隊列。

如果channel.basicNack(8, false, false);表示deliveryTag=8的消息處理失敗且將該消息直接丟棄。

  1. channel.basicReject(deliveryTag, requeue);

相比channel.basicNack,除了沒有multiple批量確認機制之外,其他語義完全一樣。

如果channel.basicReject(8, true);表示deliveryTag=8的消息處理失敗且將該消息重新放回隊列。

如果channel.basicReject(8, false);表示deliveryTag=8的消息處理失敗且將該消息直接丟棄。


參數字段類型:

  • deliveryTag:long - 消息投遞的唯一標識,作用域爲當前channel
  • multiple:boolean - 是否啓用批量確認機制
  • requeue:boolean - 消息處理失敗是重新放回隊列還是直接丟棄

Publisher Acknowledgements機制

使用標準AMQP 0-9-1協議,保證消息不丟失的唯一方法是使用事務 —— 通道事務,發佈消息並提交。在這種情況下,事務是非常重量級的操作,會使得broker消息吞吐量降低250倍左右。那麼,爲了解決使用事務確保消息不丟失所帶來的性能損耗。我們參考Consumer Acknowledgements確認機制的原理引入了Publisher Confirms確認機制。消息生產者發送消息給broker,當broker收到消息,將消息持久化到磁盤並同步至所有的鏡像節點之後,纔會返回給客戶端消息投遞成功確認。從而保證消息在投遞過程中不會因爲網絡擁塞,服務宕機,機房斷電等突發情況導致消息投遞失敗而丟失。當由於broker內部消息處理髮生異常時,將返回給客戶端basic.nack通知;當消息投遞成功時,broker則返回給客戶端basic.ack通知。

  1. 對於可用路由的消息,當所有的隊列接收到消息後,broker向client發送basic.ack確認通知;

  2. 對於路由到持久隊列的持久化消息,當消息持久化到磁盤後,broker向client發送basic.ack確認通知;

  3. 對於路由到鏡像隊列的消息,當所有的鏡像隊列都接收到消息後,broker向client發送basic.ack確認通知;

  4. 對於不可路由的消息,broker一旦確認該消息不可路由時,則向client發送basic.nack確認通知;

  5. 對於不可路由且mandatory強制投遞的消息,broker一旦確認該消息不可路由時,先向client發送basic.return通知, 然後發送basic.nack確認通知;

     basic.nack will only be delivered if an internal error occurs in the Erlang process responsible for a queue.
    

延遲確認Ack:

對於持久化消息,是需要等待消息成功持久化到磁盤之後,broker纔會返回給客戶端basic.ack通知,爲了提升IO吞吐量,broker並不會實時將消息刷回到磁盤,而是先將消息存儲到內存中,在一定時間間隔後(幾百毫秒)或當隊列空閒時,批量將消息持久化到磁盤,然後在返回給客戶端basic.ack確認通知。這就意味着在恆定負載下,basic.ack的延遲可以達到幾百毫秒。那麼,爲了提升消息系統的吞吐量,強烈建議客戶端應用程序採用異步方式處理消息basic.ack確認通知。

代碼實現

@Bean
public RabbitTemplate myRabbitTemplate(){
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    // Mandatory = true,則生產者發送消失失敗時,會先調用ReturnCallback,再調用ConfirmCallback
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
        System.out.println("消息發送失敗");
        System.out.println("message : "+message.getMessageProperties().getCorrelationId());
        System.out.println("replyCode : "+replyCode);
        System.out.println("replyText : "+replyText);
        System.out.println("exchange : "+exchange);
        System.out.println("routingKey : "+routingKey);
    });

    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
        String id = correlationData.getId();
        if(ack){
            // 成功發送到隊列後後調(與消費者是否成功消費無關)
            System.out.println("消息 :"+id+"  收到確認信息");
        }else {
            // basic.nack will only be delivered if an internal error
            // occurs in the Erlang process responsible for a queue.
            // 僅在Erlang發生內部錯誤,未能將消息發送至隊列時調用
            System.out.println("消息 :"+id+"  發送失敗");
            System.out.println("casue :"+cause);
        }
    });
    return rabbitTemplate;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章