當消費者從消息隊列中拉取了一條消息,去處理某個業務的時候出現了異常,那這條消息沒有被正確消費的時候我們該怎麼處理呢?
rabbitmq有一個確認機制
首先看一下如果我們不做確認的話是一種什麼情況
public class ConnectionUtil {
public static String QUEUE_NAME = "testQueue";
public static String EXCHANGE_NAME = "testExchange";
public static Connection getConnection() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
//設置服務端所在地址
factory.setHost("127.0.0.1");
//設置端口號
factory.setPort(5672);
//設置用戶名
factory.setUsername("helloWorld");
//設置密碼
factory.setPassword("helloWorld");
//設置虛擬地址
factory.setVirtualHost("testHost");
return factory.newConnection();
}
}
public class Send {
public static void main(String[] args) throws Exception{
//獲取連接
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//聲明隊列
channel.queueDeclare(ConnectionUtil.QUEUE_NAME,false,false,false,null);
//聲明交換機
channel.exchangeDeclare(ConnectionUtil.EXCHANGE_NAME,"fanout");
//交換機和隊列綁定
channel.queueBind(ConnectionUtil.QUEUE_NAME,ConnectionUtil.EXCHANGE_NAME,"helloWorld");
channel.basicPublish(ConnectionUtil.EXCHANGE_NAME,"",null,"helloWorld".getBytes());
System.out.println("發送的信息爲:helloWorld");
channel.close();
connection.close();
}
}
public class Recv {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
}
};
//消費消息,沒有確認
channel.basicConsume(ConnectionUtil.QUEUE_NAME, deliverCallback);
}
}
當發送一條消息的時候,testQueue中有一條消息是ready狀態
然後消費消息的時候,我沒有做確認,消費完這條消息,消息還在這個隊列,只是狀態變成了Unacked
如果消費者不做消息確認的話,在消費端和服務器斷開的時候Unacked狀態的消息又會重回到Ready狀態,
這麼做肯定不行,因爲長時間下來隊列中消息會越來越多,造成消息大量堆積
這裏消息確認有兩種方式
自動確認
自動確認是在消費者一拉去到消息就做了確認,不管消費者有沒有成功消費,來看這個測試
消費者是成功拉去到了消息,但是並沒有正常消費,而消息系統卻刪除了這條消息,顯然是和我們正常業務需求是不相符到
public class Recv {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(1/0);
System.out.println(new String(body, "UTF-8"));
}
};
//消費消息,並自動確認
channel.basicConsume(ConnectionUtil.QUEUE_NAME,true, deliverCallback);
}
}
手動確認
當在確認之前有異常發生,消息沒有被成功消費當時候,隊列中消息的狀態依舊爲Ready狀態,業務上可以在發生異常的時候進行重試幾次
public class Recv {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
System.out.println(1/0);
//這裏進行確認,第一個參數爲消息的標識符,第二個參數爲是否批量確認
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(ConnectionUtil.QUEUE_NAME,false, deliverCallback);
}
}
當消費者成功消費,手動確認之後
public class Recv {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
//這裏進行確認,第一個參數爲消息的標識符,第二個參數爲是否批量確認
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(ConnectionUtil.QUEUE_NAME,false, deliverCallback);
}
}