消息确认机制
生产者将消息发送出去后,是否到达rabbitmq服务器默认不知道,通过两种方式,AMQP实现了事务机制和Confirm模式。
事务机制
使用协议,但是降低了rabbitmq的消息吞吐量
- txSelect:当前channel设置成transaction模式
- txCommit:提交事务
- txRollback:回滚事务
public class TxProduct {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg = "tx msg";
//开启
try {
channel.txSelect();
for (int i = 0; i < 10; i++) {
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
if (i == 4) {
int n = 1 / 0;
}
}
channel.txCommit();
} catch (IOException e) {
channel.txRollback();
System.out.println("rollback");
e.printStackTrace();
} finally {
channel.close();
connection.close();
}
}
}
Confirm模式
Confirm模式:
- 将
channel
设置为confirm模式,在该信道发布的消息都会被指派唯一ID - 消息被队列匹配,broker发送确认给生产者包含ID,让生产者知道消息正确到达。
- Confirm最大的好处是异步,生产者可以持续发送消息
- 如果当前队列是事务模式,则不能进行修改,只能设定新的队列
持久化:
- 确认消息会在消息写入磁盘之后发出
- broker发送的确认消息中deliver-tag包含了确认消息的序列号
basicAck
中的multiple
参数表示,这个序列号之前的所有消息已经得到处理
生产者
单条消息发送,使用confirmSelect
声明,使用wairForConfirms
等待回复
public class ConfirmProduct {
private static final String QUEUE_NAME="test_queue_confirm";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String msg = "confirm msg";
//将channel设置为confirm模式
channel.confirmSelect();
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
//如果没有confirm 发送失败
if (!channel.waitForConfirms()){
System.out.println("message send fail");
} else {
System.out.println("message send success");
}
channel.close();
connection.close();
}
}
异步生产
public class AsynConfirmProduct {
private static final String QUEUE_NAME = "test_queue_asynconfirm";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.confirmSelect();
//存放未确认消息
final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
//通道添加监听
channel.addConfirmListener(new ConfirmListener() {
//没问题的handleack
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.print(deliveryTag);
if (multiple){//多条消息,clear所有
System.out.println("---handleAck---multiple");
//headSet返回严格小于deliveryTag+1 的集合,并且清除
confirmSet.headSet(deliveryTag+1).clear();
} else{//单条消息,直接remove此条
System.out.println("---handleAck---multiple false");
confirmSet.remove(deliveryTag);
}
}
//有问题的操作
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if (multiple){
System.out.println("---handleNAck---multiple");
confirmSet.headSet(deliveryTag+1).clear();
} else {
System.out.println("---handleNAck---multiple false");
confirmSet.remove(deliveryTag);
}
}
});
for (int i = 0; i < 10; i++) {
String msg = "Asynchronous message" + i;
long seqNo = channel.getNextPublishSeqNo();
System.out.println("Send: "+msg);
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
confirmSet.add(seqNo);
}
}
}
发了十条消息,只返回四次,都是多次返回值
这一情况是,最后一次返回单次消息,不是multiple的。
区别
- 出现异常,事务中的所有消息都不会发送,但是confirm是异常后的消息不发送,异常之前的消息,正常发送。
- confirm效率相对于事务来说比较高,建议使用