消息发送确认
https://www.jianshu.com/p/0db95a3e972c
1.事务,利用AMQP协议的一部分,发送消息前设置channel为tx模式(channel.txSelect();),如果txCommit提交成功了,则消息一定到达了broker了,如果在txCommit执行之前broker异常崩溃或者由于其他原因抛出异常,这个时候我们便可以捕获异常通过txRollback回滚事务了。(大大得削弱消息中间件的性能)
2.消息确认(publish confirms),设置管道为confirmSelect模式
https://blog.csdn.net/convict_eva/article/details/52442125
认并且保证消息被送达,提供了两种方式:发布确认和事务。(两者不可同时使用)在channel为事务时,不可引入确认模式;同样channel为确认模式下,不可使用事务。
发布确认:
Confirms给客户端一种轻量级的方式,能够跟踪哪些消息被broker处理,哪些可能因为broker宕掉或者网络失败的情况而重新发布。
确认消息是否到达broker服务器,也就是只确认是否正确到达exchange中即可,只要正确的到达exchange中,broker即可确认该消息返回给客户端ack。
有两种方式:消息的确认和消息发送失败的回调。
事务Spring AMQP做的不仅仅是回滚事务,而且可以手动拒绝消息,如当监听容器发生异常时是否重新入队。
持久化的消息是应该在broker重启前都有效。如果在消息有机会写入到磁盘之前broker宕掉,消息仍然会丢失。在某些情况下,这是不够的,发布者需要知道消息是否处理正确。简单的解决方案是使用事务,即提交每条消息。
问题:使用Spring AMQP事务有两个问题:
一、会阻塞,发布者必须等待broker处理每个消息。如果发布者知道在broker死掉之前哪些消息没有被处理就足够了。
二、事务是重量级的,每次提交都需要fsync(),需要耗费大量的时间。
confirm模式下,broker将会确认消息并处理。这种模式下是异步的,生产者可以流水式的发布而不用等待broker,broker可以批量的往磁盘写入。
本文来自 Jamin_Ma 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/convict_eva/article/details/52442125?utm_source=copy
本文来自 Jamin_Ma 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/convict_eva/article/details/52442125?utm_source=copy
消息确认机制测试代码
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* @author: yangwenkang
* @date: 2018/09/27
* @description:
**/
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//第二个参数表示是否手动确认,第三参数即回调函数,具体实现消费消息后怎么处理确认消息
//public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {
String consumerTag = channel.basicConsume("zhihao.miao.order", false, new SimpleConsumer(channel));
public class SimpleConsumer extends DefaultConsumer {
public SimpleConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
if (properties.getHeaders().get("error") != null) {
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
//处理失败的消息,可以使用basicReject,也可使用basicNack (区别在于后者可以批量处理)
//1 这个api也支持拒绝消息消费,第二个参数表示是否重新入队列
// public void basicReject(long deliveryTag, boolean requeue) throws IOException {
this.getChannel().basicReject(envelope.getDeliveryTag(), false);
//这个api public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException {
// this.getChannel().basicNack(envelope.getDeliveryTag(),false,false);
return;
}
// 获取消息byte[] body,这里是处理消息的业务逻辑,1消费失败可以这里Nack, 2消费成功则可以backAck确认
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//2 这个api也支持消费成功,ack确认消息,第二个参数表示是否批量确认
// public void basicAck(long deliveryTag, boolean multiple) throws IOException {
this.getChannel().basicAck(envelope.getDeliveryTag(), false);
}
}
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.util.concurrent.TimeUnit;
/**
* @author: yangwenkang
* @date: 2018/09/27
* @description:
**/
public class TestRabbitMqConsumerAcknowledgement {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setUri("amqp://zhihao.miao:123456@192.168.1.131:5672");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//第二个参数表示是否手动确认,第三参数即回调函数,具体实现消费消息后怎么处理确认消息
//public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {
String consumerTag = channel.basicConsume("zhihao.miao.order", false, new SimpleConsumer(channel));
System.out.println(consumerTag);
TimeUnit.SECONDS.sleep(30);
channel.close();
connection.close();
}
}
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* @author: yangwenkang
* @date: 2018/09/27
* @description:
**/
public class SimpleConsumer extends DefaultConsumer {
public SimpleConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
if (properties.getHeaders().get("error") != null) {
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
//处理失败的消息,可以使用basicReject,也可使用basicNack (区别在于后者可以批量处理)
//1 这个api也支持拒绝消息消费,第二个参数表示是否重新入队列
// public void basicReject(long deliveryTag, boolean requeue) throws IOException {
this.getChannel().basicReject(envelope.getDeliveryTag(), false);
//这个api public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException {
// this.getChannel().basicNack(envelope.getDeliveryTag(),false,false);
return;
}
// 获取消息byte[] body,这里是处理消息的业务逻辑,1消费失败可以这里Nack, 2消费成功则可以backAck确认
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//2 这个api也支持消费成功,ack确认消息,第二个参数表示是否批量确认
// public void basicAck(long deliveryTag, boolean multiple) throws IOException {
this.getChannel().basicAck(envelope.getDeliveryTag(), false);
}
}