本文是参考下面这篇博客然后进行复现浓缩后的总结
https://www.cnblogs.com/vipstone/p/9350075.html
正常情况下,如果消息经过交换器进入队列就可以完成消息的持久化,但如果消息在没有到达broker之前出现意外,那就造成消息丢失,有没有办法可以解决这个问题?RabbitMQ有两种方式来解决这个问题:
事物的实现主要是对于信道(Channel)的设置,其中主要的方法有三个:
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 队列名, 持久化, 是否排外, 非自动删除
channel.queueDeclare("queueName",true, false, false, null);
String message = "这是一个测试消息";
try {
channel.txSelect();
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("exchangeName", "queueName", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));
channel.txCommit();
}catch (Exception e){
channel.txRollback();
}finally {
channel.close();
connection.close();
}
}
假设消费者模式中使用了事务,并且在消息确认之后进行了事务回滚,那么RabbitMQ会产生什么样的变化?结果分为两种情况:
Confirm发送方确认模式使用和事务类似,也是通过设置Channel进行发送方确认的。Confirm的三种实现方式:
方式一:channel.waitForConfirms()普通发送方确认模式;
方式二:channel.waitForConfirmsOrDie()批量确认模式;
方式三:channel.addConfirmListener()异步监听发送方确认模式;
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 队列名, 持久化, 是否排外, 非自动删除
channel.queueDeclare("queueName",true, false, false, null);
String message = "这是一个测试消息";
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("exchangeName", "queueName", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));
if(channel.waitForConfirms()){
System.out.println("消息发送成功");
}
}
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 队列名, 持久化, 是否排外, 非自动删除
channel.queueDeclare("queueName",true, false, false, null);
channel.confirmSelect();
for(int i = 0; i < 10; i++){
String message = "这是一个测试消息" + i;
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("exchangeName", "queueName", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));
}
channel.waitForConfirmsOrDie();//对所有消息进行等待,只要有一个未发送就会返回异常
}
public void test2() throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 队列名, 持久化, 是否排外, 非自动删除
channel.queueDeclare("queueName",true, false, false, null);
channel.confirmSelect();
for(int i = 0; i < 10; i++){
String message = "这是一个测试消息" + i;
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("exchangeName", "queueName", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));
}
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long l, boolean b) throws IOException {
System.out.println("消息发送失败,标识:" + l);
}
@Override
public void handleNack(long l, boolean b) throws IOException {
System.out.println(String.format("消息发送成功,标识:%d, 是否是多个同时确认:%b", l, b));
}
});
}
可以看出,代码是异步执行的,消息确认有可能是批量确认的,是否批量确认在于返回的multiple的参数,此参数为bool值,如果true表示批量执行了deliveryTag这个值以前的所有消息,如果为false的话表示单条确认。
Confirm批量确定和Confirm异步模式性能相差不大,Confirm模式要比事务快10倍左右。