RabbitMQ 消息確認

正常情況下,如果消息經過交換器進入隊列就可以完成消息的持久化,但如果消息在沒有到達broker之前出現意外,那就造成消息丟失,有沒有辦法可以解決這個問題?

RabbitMQ有兩種方式來解決這個問題:
一、事務使用
事務的實現主要是對信道(Channel)的設置,主要的方法有三個:
channel.txSelect()聲明事務模式;
channel.txComment()提交事務;
channel.txRollback()回滾事務;
// 創建連接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(config.UserName);
factory.setPassword(config.Password);
factory.setVirtualHost(config.VHost);
factory.setHost(config.Host);
factory.setPort(config.Port);  
Connection conn = factory.newConnection();
// 創建信道
Channel channel = conn.createChannel();
// 聲明隊列
channel.queueDeclare(_queueName, true, false, false, null);
String message = String.format("時間 => %s", new Date().getTime());
try {
   channel.txSelect(); // 聲明事務
   // 發送消息
   channel.basicPublish("", _queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"));
   channel.txCommit(); // 提交事務
} catch (Exception e) {
   channel.txRollback();
} finally {
   channel.close();
   conn.close();
}


二、Confirm發送方確認模式
Confirm發送方確認模式使用和事務類似,也是通過設置Channel進行發送方確認的。

Confirm的三種實現方式:
方式一:channel.waitForConfirms()普通發送方確認模式;
方式二:channel.waitForConfirmsOrDie()批量確認模式;
方式三:channel.addConfirmListener()異步監聽發送方確認模式;

方式一:普通Confirm模式
// 創建連接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(config.UserName);
factory.setPassword(config.Password);
factory.setVirtualHost(config.VHost);
factory.setHost(config.Host);
factory.setPort(config.Port);
Connection conn = factory.newConnection();
// 創建信道
Channel channel = conn.createChannel();
// 聲明隊列
channel.queueDeclare(config.QueueName, false, false, false, null);
// 開啓發送方確認模式
channel.confirmSelect();
String message = String.format("時間 => %s", new Date().getTime());
channel.basicPublish("", config.QueueName, null, message.getBytes("UTF-8"));
if (channel.waitForConfirms()) {
   System.out.println("消息發送成功" );
}
看代碼可以知道,我們只需要在推送消息之前,channel.confirmSelect()聲明開啓發送方確認模式,再使用channel.waitForConfirms()等待消息被服務器確認即可。

方式二:批量Confirm模式

// 創建連接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(config.UserName);
factory.setPassword(config.Password);
factory.setVirtualHost(config.VHost);
factory.setHost(config.Host);
factory.setPort(config.Port);
Connection conn = factory.newConnection();
// 創建信道
Channel channel = conn.createChannel();
// 聲明隊列
channel.queueDeclare(config.QueueName, false, false, false, null);
// 開啓發送方確認模式
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
   String message = String.format("時間 => %s", new Date().getTime());
   channel.basicPublish("", config.QueueName, null, message.getBytes("UTF-8"));
}
channel.waitForConfirmsOrDie(); //直到所有信息都發布,只要有一個未確認就會IOException
System.out.println("全部執行完成");
以上代碼可以看出來channel.waitForConfirmsOrDie(),使用同步方式等所有的消息發送之後纔會執行後面代碼,只要有一個消息未被確認就會拋出IOException異常。

方式三:異步Confirm模式
// 創建連接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(config.UserName);
factory.setPassword(config.Password);
factory.setVirtualHost(config.VHost);
factory.setHost(config.Host);
factory.setPort(config.Port);
Connection conn = factory.newConnection();
// 創建信道
Channel channel = conn.createChannel();
// 聲明隊列
channel.queueDeclare(config.QueueName, false, false, false, null);
// 開啓發送方確認模式
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
   String message = String.format("時間 => %s", new Date().getTime());
   channel.basicPublish("", config.QueueName, null, message.getBytes("UTF-8"));
}
//異步監聽確認和未確認的消息
channel.addConfirmListener(new ConfirmListener() {
   @Override
   public void handleNack(long deliveryTag, boolean multiple) throws IOException {
      System.out.println("未確認消息,標識:" + deliveryTag);
   }
   @Override
   public void handleAck(long deliveryTag, boolean multiple) throws IOException {
      System.out.println(String.format("已確認消息,標識:%d,多個消息:%b", deliveryTag, multiple));
   }
});
異步模式的優點,就是執行效率高,不需要等待消息執行完,只需要監聽消息即可,以上異步返回的信息如下:可以看出,代碼是異步執行的,消息確認有可能是批量確認的,是否批量確認在於返回的multiple的參數,此參數爲bool值,如果true表示批量執行了deliveryTag這個值以前的所有消息,如果爲false的話表示單條確認。

注意:
1. RabbitMQ中的事務機制與大多數數據庫中的事務概念並不相同,需要注意區分。
2. ack或者nack只發一次,不會出現一條消息即被ack又被nack的情況
3.事務機制和publisher confirm 機制兩者是互斥的:
    信道已開啓事務模式再開啓 publisher confirm模式,
    報錯:{amqp_error, precondition_failed, "cannot switch from tx to confirm mode", 'confirm.select'}
    信道已開啓publisher confirm模式再設置事務模式
    報錯:{amqp_error, precondition_failed, "cannot switch from confirm to tx mode", 'tx.select' }
4.消息投遞到交換器機,交換機沒有找到匹配的隊列的話,那麼消息也將會丟失。發送方要配合mandatory參數或者備份交換器一起使用來提高消息傳輸的可靠性。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章