我们知道可以通过持久化(交换机、队列和消息持久化)来保障我们在服务器崩溃时,重启服务器消息数据不会丢失。但是我们无法确认当消息的发布者在将消息发送出去之后,消息到底有没有正确到达Broker代理服务器呢?如果不进行特殊配置的话,默认情况下发布操作是不会返回任何信息给生产者的,也就是默认情况下我们的生产者是不知道消息有没有正确到达Broker的。如果在消息到达Broker之前已经丢失的话,持久化操作也解决不了这个问题,因为消息根本就没到达代理服务器,这个是没有办法进行持久化的,那么当我们遇到这个问题又该如何去解决呢?
这里就是我们讲解到的RabbitMQ中的消息确认机制,通过消息确认机制我们可以确保我们的消息可靠送达到我们的用户手中,即使消息丢失掉,我们也可以通过进行重复分发确保用户可靠收到消息。
今天我们讲解的RabbitMQ消息确认机制,主要包括两个方面,因为RabbitMQ为我们提供了两种方式:
- 通过AMQP事务机制实现,这也是AMQP协议层面提供的解决方案;
- 通过将channel设置成confirm模式来实现;
AMQP事务
我们知道事务可以保证消息的传递,使得可靠消息最终一致性。接下来我们先来探究一下RabbitMQ的事务机制。
RabbitMQ中与事务有关的主要有三个方法:
txSelect()
txCommit()
txRollback()
txSelect主要用于将当前channel设置成transaction模式,txCommit用于提交事务,txRollback用于回滚事务。
当我们使用txSelect提交开始事务之后,我们就可以发布消息给Broke代理服务器,如果txCommit提交成功了,则消息一定到达了Broke了,如果在txCommit执行之前Broker出现异常崩溃或者由于其他原因抛出异常,这个时候我们便可以捕获异常通过txRollback方法进行回滚事务了。
所以RabbitMQ事务中的主要代码为:
channel.txSelect();
channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
channel.txCommit();
新建连接RabbitMQ的工具类utils
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class utils {
public static Connection getConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setVirtualHost("/vhost_zdy");
factory.setUsername("username");
factory.setPassword("password");
return factory.newConnection();
}
}
新建生产者类TxSend,用1/0模拟异常
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TxSend {
private final static String QUEUE_NAME = "TxTest";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = utils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg="TxMsg";
try {
channel.txSelect();
int i=1/0;//出错情况
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
channel.txCommit();
}
catch (Exception e){
channel.txRollback();
System.out.println("tx rollback");
}
channel.close();
connection.close();
}
}
新建消费者类TxReceive
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TxReceive {
private final static String QUEUE_NAME = "TxTest";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = utils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages.");
DefaultConsumer defaultConsumer= new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"UTF-8");
System.out.println("receive:"+ msg);
}
};
channel.basicConsume(QUEUE_NAME,defaultConsumer);
}
}
异常时回滚,消费者未收到消息
Confirm模式
- 普通Confirm模式:每发送一条消息后,调用waitForConfirms()方法,等待服务器端Confirm。实际上是一种串行Confirm了,每publish一条消息之后就等待服务端Confirm,如果服务端返回false或者超时时间内未返回,客户端进行消息重传;
- 批量Confirm模式:批量Confirm模式,每发送一批消息之后,调用waitForConfirms()方法,等待服务端Confirm,这种批量确认的模式极大的提高了Confirm效率,但是如果一旦出现Confirm返回false或者超时的情况,客户端需要将这一批次的消息全部重发,这会带来明显的重复消息,如果这种情况频繁发生的话,效率也会不升反降;
- 异步Confirm模式:提供一个回调方法,服务端Confirm了一条或者多条消息后Client端会回调这个方法。
https://blog.csdn.net/Anumbrella/article/details/81321701