目的:保证在发送成功或者失败的情况下都能获取相关发送信息。
三种发布确认中:
单个确认为每发送一次消息就进行一次确认,优点是准确无误,缺点是资源占用较大,速度较慢;
代码:
/**
* 单个确认发布
* @throws Exception
*/
public static void publishMessageIndividually() throws Exception{
Channel channel = RabbitMqUtils.getChannel();
//声明队列
String queueName = UUID.randomUUID().toString();
channel.queueDeclare(queueName,true,false,false,null);
//开启发布确认
channel.confirmSelect();
//开始时间
long begin = System.currentTimeMillis();
//批量发消息
for (int i = 0; i < MESSAGE_COUNT; i++) {
String message = i + "";
channel.basicPublish("",queueName,null,message.getBytes());
//单个消息马上进行发布确认
boolean flag = channel.waitForConfirms();
if (flag){
System.out.println("消息发送成功");
}
}
//结束时间
long end = System.currentTimeMillis();
//打印用时
System.out.println("发布" + MESSAGE_COUNT + "个单独确认消息,耗时 " + (end - begin)+" ms");
}
批量确认为每发送一批消息再进行一次确认,优点是比单个确认更快,但是无法精确定位到发送失败消息。
代码:
/**
* 批量发布确认
* @throws Exception
*/
public static void publicMessageBatch() throws Exception{
Channel channel = RabbitMqUtils.getChannel();
//声明队列
String queueName = UUID.randomUUID().toString();
channel.queueDeclare(queueName,true,false,false,null);
//开启发布确认
channel.confirmSelect();
//开始时间
long begin = System.currentTimeMillis();
//批量确认消息大小
int batchSize = 1000;
//批量发送消息 批量发布确认
for (int i = 0; i < MESSAGE_COUNT; i++) {
String message = i + "";
channel.basicPublish("",queueName,null,message.getBytes());
//判断达到100条消息的时候 批量确认一次
if ((i+1)%batchSize == 0){
//发布确认
channel.waitForConfirms();
}
}
//结束时间
long end = System.currentTimeMillis();
//打印用时
System.out.println("发布" + MESSAGE_COUNT + "个批量确认消息,耗时 " + (end - begin)+" ms");
}
异步确认为在发送前创建一个支持高并发的Map,key存储消息tag,values存储message,并调用监听器进行监听消息发送。一般在每次发送后用Map记录下发送消息,监听器根据结果回调相关函数,若发送成功,回调成功函数,在Map中删去该消息。发送失败,回调失败函数,在失败函数中通过Map显示该消息。异步确认在所有确认中综合性能最佳。
代码:
/**
* 异步发布确认
*/
public static void publishMessageAsync() throws Exception{
Channel channel = RabbitMqUtils.getChannel();
//声明队列
String queueName = UUID.randomUUID().toString();
channel.queueDeclare(queueName,true,false,false,null);
//开启发布确认
channel.confirmSelect();
/**
* 线程安全有序的一个哈希表 适用于高并发的情况下
* 1、轻松的将序号与消息进行关联
* 2、轻松批量删除条目 只要给到序号
* 3、支持高并发(多线程)
*/
ConcurrentSkipListMap<Long,String> outstandingConfirms = new ConcurrentSkipListMap<>();
//消息确认成功 回调函数
ConfirmCallback ackCallback = (deliveryTag,multiple) -> {
// 2、删除掉已经确认的消息 剩下的就是未确认的消息
if (multiple){//是否是批量消息
ConcurrentNavigableMap<Long, String> confirmed = outstandingConfirms.headMap(deliveryTag);
confirmed.clear();
}
else {
outstandingConfirms.remove(deliveryTag);
}
System.out.println("确认的消息 :" + deliveryTag);
};
//消息确认失败 回调函数
/**
* 参数 :
* 1、消息的标记
* 2、是否为批量确认
*/
ConfirmCallback nackCallback = (deliveryTag,multiple) -> {
// 3、打印下未确认的消息都有哪些
String message = outstandingConfirms.get(deliveryTag);
System.out.println("未确认的消息tag :" + deliveryTag + "未确认消息是:"+message);
};
//准备消息的监听器 监听那些消息成功了 哪些消息失败了 异步监听
/**
* 参数
* 1、监听哪些消息成功了
* 2、监听哪些消息失败了
*/
channel.addConfirmListener(ackCallback,nackCallback);
//开始时间
long begin = System.currentTimeMillis();
//批量发送消息
for (int i = 0; i < MESSAGE_COUNT; i++) {
String message = "消息" + i;
channel.basicPublish("",queueName,null,message.getBytes());
// 1、记录下所有要发送的消息
outstandingConfirms.put(channel.getNextPublishSeqNo(),message);
}
//结束时间
long end = System.currentTimeMillis();
//打印用时
System.out.println("发布" + MESSAGE_COUNT + "个异步确认消息,耗时 " + (end - begin)+" ms");
}
三种确认之前都需要先开启发布确认。
channel.confirmSelect();