參考:https://www.cnblogs.com/leeSmall/p/9653219.html
一、隊列關於ACK的設置
消費者在聲明隊列時,可以指定noAck參數,當noAck=false時,RabbitMQ會等待消費者顯式發回ack信號後才從內存(和磁盤,如果是持久化消息的話)中移去消息。否則,RabbitMQ會在隊列中消息被消費後立即刪除它。
二、消費端關於ACK的設置
RabbitMQ不會爲未ack的消息設置超時時間,它判斷此消息是否需要重新投遞給消費者的唯一依據是消費該消息的消費者連接是否已經斷開。這麼設計的原因是RabbitMQ允許消費者消費一條消息的時間可以很久很久。
這個在channe.basicConsume方法中設置自動ACK還是手動ACK
1.自動 acknowledgement 模式
發後即忘
只要rabbitmq投遞了消息給消費者,就認爲投遞成功,不管消費者是否真的收到了
這樣可能會丟消息
channel.basicConsume(queueName,true,consumerB);
2.手動 acknowledgement 模式
rabbitmq投遞了消息給消費者,需要消費者返回ACK,沒有超時限制。
如果消費者的 TCP 連接 或者 channel 關閉了,導致這條消息沒有被 acked,RabbitMQ 會自動把當前消息重新入隊,再次投遞。
重點!:如果消費者沒發送ACK,沒有超時限制,rabbitmq也不會重複投遞,只有消費者的 TCP 連接 或者 channel 關閉了,rabbitmq纔會重複投遞!
這樣可能出現重複投遞消息,消費端需要做好冪等性。
//設置一個監聽器監聽消費消息
Consumer consumerB = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body,"UTF-8");
System.out.println("Accept:"+envelope.getRoutingKey()+":"+message);
//消費者自行確認
this.getChannel().basicAck(envelope.getDeliveryTag(),false);
}
};
//消費者自行確認:autoAck參數爲false
channel.basicConsume(queueName,false,consumerB);
其中可能用到的方法和入參的解釋
方法:
channel.basicAck(long deliveryTag, boolean multiple) throws IOException;
代表消費者確認收到當前消息,語義上表示消費者成功處理了當前消息。
如果消費者沒發送ACK,沒有超時限制,rabbitmq也不會重複投遞,只有消費者的 TCP 連接 或者 channel 關閉了,rabbitmq纔會重複投遞!
channel.basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException
代表消費者拒絕一條或者多條消息。basicNack 算是 basicReject 的一個擴展,因爲 basicReject 不能一次拒絕多條消息。
channel.basicReject(long deliveryTag, boolean requeue) throws IOException;
代表消費者拒絕這條消息,語義上表示消費者沒有處理當前消息。
對於 basicNack 和 basicReject ,如果參數 boolean requeue 傳入 false,消息還是會從隊列裏面刪除。
入參:
deliveryTag
當消費者向RabbitMQ註冊後,RabbitMQ使用basic.deliver向消費者投遞消息時,消息體上會帶上delivery tag,這個值會唯一標識本次投遞,在同一通道上,此值是唯一的。delivery tag值有64位長度,值從1開始,每發送一次消息值遞增1,最大值爲9223372036854775807。消費者端在應答消息時,帶上此參數,告訴RabbitMQ某次投遞已經正確應答。
multiple
是否使用批量確認,如果此屬性爲true,則消息標籤小於當前deliveryTag的所有消息都會被主動積極確認,不瞭解此屬性最好使用false。
requeue
是否重新入隊,如果此屬性爲true,消息會被重新放置回去對應隊列(如果可能的話,會放回到原來的位置),如果此屬性爲false,消息直接被丟棄。
屬性requeue
如果設置爲true,需要謹慎設計程序的邏輯,否則很有可能導致消息一直重複消費失敗並且重複重新入隊,表現爲消費者線程出現死循環邏輯,耗盡服務器CPU資源。