消息确认机制ACK
消息確認機制(ACK)
通過剛才的案例可以看出,消息一旦被消費者接收,隊列中的消息就會被刪除。
那么問題來了:RabbitMQ怎么知道消息被接收了呢?
如果消費者領取消息后,還沒執行操作就掛掉了呢?或者拋出了異常?消息消費失敗,但是RabbitMQ無從得知,這樣消息就丟失了!
因此,RabbitMQ有一個ACK機制。當消費者獲取消息后,會向RabbitMQ發送回執ACK,告知消息已經被接收。不過這種回執ACK分兩種情況:
-
自動ACK:消息一旦被接收,消費者自動發送ACK
-
手動ACK:消息接收后,不會發送ACK,需要手動調用
大家覺得哪種更好呢?
這需要看消息的重要性:
-
如果消息不太重要,丟失也沒有影響,那么自動ACK會比較方便
-
如果消息非常重要,不容丟失。那么最好在消費完成后手動ACK,否則接收消息后就自動ACK,RabbitMQ就會把消息從隊列中刪除。如果此時消費者宕機,那么消息就丟失了。
我們之前的測試都是自動ACK的,如果要手動ACK,需要改動我們的代碼:
public class Recv2 {private final static String QUEUE_NAME = "simple_queue";public static void main(String[] argv) throws Exception {// 獲取到連接Connection connection = ConnectionUtil.getConnection();// 創建通道final Channel channel = connection.createChannel();// 聲明隊列channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 定義隊列的消費者DefaultConsumer consumer = new DefaultConsumer(channel) {// 獲取消息,并且處理,這個方法類似事件監聽,如果有消息的時候,會被自動調用@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,byte[] body) throws IOException {// body 即消息體String msg = new String(body);System.out.println(" [x] received : " + msg + "!");// 手動進行ACKchannel.basicAck(envelope.getDeliveryTag(), false);}};// 監聽隊列,第二個參數false,手動進行ACKchannel.basicConsume(QUEUE_NAME, false, consumer);} }注意到最后一行代碼:
channel.basicConsume(QUEUE_NAME, false, consumer);如果第二個參數為true,則會自動進行ACK;如果為false,則需要手動ACK。方法的聲明:
自動ACK存在的問題
修改消費者,添加異常,如下:
生產者不做任何修改,直接運行,消息發送成功:
運行消費者,程序拋出異常。但是消息依然被消費:
演示手動ACK
修改消費者,把自動改成手動(去掉之前制造的異常)
生產者不變,再次運行:
運行消費者
但是,查看管理界面,發現:
停掉消費者的程序,發現:
這是因為雖然我們設置了手動ACK,但是代碼中并沒有進行消息確認!所以消息并未被真正消費掉。
當我們關掉這個消費者,消息的狀態再次稱為Ready
?
修改代碼手動ACK:
執行:
消息消費成功!
總結