Consumer Acknowledgements機制
當consumer消費端成功消費完消息後,返回給broker確認通知,告訴broker移除隊列中已經消費成功的消息,如果消費端消費失敗,可以通知broker將消費失敗的消息重新放回隊列中,以便繼續消費。
- 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的消息已經成功處理。
- 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的消息處理失敗且將該消息直接丟棄。
- 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通知。
-
對於可用路由的消息,當所有的隊列接收到消息後,broker向client發送basic.ack確認通知;
-
對於路由到持久隊列的持久化消息,當消息持久化到磁盤後,broker向client發送basic.ack確認通知;
-
對於路由到鏡像隊列的消息,當所有的鏡像隊列都接收到消息後,broker向client發送basic.ack確認通知;
-
對於不可路由的消息,broker一旦確認該消息不可路由時,則向client發送basic.nack確認通知;
-
對於不可路由且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;
}