rabbitmq发布确认
rabbitmq发布确认
发布确认原理
生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker 就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了
如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置basic.ack 的multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。
1、单个发布确认
这是一种简单的确认方式,它是一种同步确认发布的方式,也就是发布一个消息之后只有它被确认发布,后续的消息才能继续发布,waitForConfirmsOrDie(long)这个方法只有在消息被确认的时候才返回,如果在指定时间范围内这个消息没有被确认那么它将抛出异常
这种确认方式有一个最大的缺点就是:发布速度特别的慢,因为如果没有确认发布的消息就会阻塞所有后续消息的发布,这种方式最多提供每秒不超过数百条发布消息的吞吐量。当然对于某些应用程序来说这可能已经足够了
2、批量发布确认
与单个等待确认消息相比,先发布一批消息然后一起确认可以极大地提高吞吐量,当然这种方式的缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布消息。当然这种方案仍然是同步的,也一样阻塞消息的发布
代码实现
package com.yl.confirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yl.util.RabbitConstant;
import com.yl.util.RabbitUtils;
import java.io.IOException;
/**
* 发布确认
* 注意:这里的确认指的是确认发布,即确认消息被发送到队列中了,而不是确认消息被消费成功
* 单个发布确认:发布一个消息之后确认消息是否发布成功(发布失败,后续代码依然可以执行)
* 批量发布确认:先发布一批消息然后一起确认消息是否发布成功
*
* @author Y-wee
*/
public class ProviderSingle {
public static void main(String[] args) throws IOException, InterruptedException {
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
// 开启发布确认
channel.confirmSelect();
channel.queueDeclare(RabbitConstant.QUEUE_CONFIRM, false, false, false, null);
for (int i = 0; i < 100; i++) {
String message=i+"";
channel.basicPublish("",RabbitConstant.QUEUE_CONFIRM,null,message.getBytes());
// 确认消息是否发布成功,若(若要批量确认则判断发布多条消息后再执行该方法)
channel.waitForConfirms();
System.out.println("第"+i+"条消息发送成功");
}
}
}
RabbitUtils是笔者自定义的一个工具类,用来获取连接
RabbitConstant是笔者自定义的一个常量类,里面存储了队列名和交换机名
3、异步发布确认
异步确认虽然编程逻辑比上两个要复杂,但是性价比最高,无论是可靠性还是效率都没得说,他是利用回调函数来达到消息可靠性传递的
代码实现
package com.yl.confirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;
import com.rabbitmq.client.Connection;
import com.yl.util.RabbitConstant;
import com.yl.util.RabbitUtils;
import java.io.IOException;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
/**
* 异步发布确认
*
* @author Y-wee
*/
public class ProviderAsync {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
channel.confirmSelect();
channel.queueDeclare(RabbitConstant.QUEUE_CONFIRM, false, false, false, null);
/**
* 线程安全有序的一个哈希表,适用于高并发
* 存储消息(Long-消息标记,String-消息),方便发布确认失败回调函数获取发布失败的消息并处理
*/
ConcurrentSkipListMap<Long,String> map=new ConcurrentSkipListMap();
/**
* 发布确认成功回调函数
* deliveryTag:消息标记,如果是批量的则该标记是最后一条消息的
* multiple:是否批量确认
*/
ConfirmCallback ackCallback=(deliveryTag,multiple)->{
// 获取发布确认成功的消息并从map中删除
if (multiple){
// 获取key<deliveryTag的值,deliveryTag+1即获取所有发布确认成功的消息
ConcurrentNavigableMap<Long,String> confirmMap=map.headMap(deliveryTag+1);
confirmMap.clear();
}else {
map.remove(deliveryTag);
}
System.out.println(deliveryTag+"发布成功");
};
// 发布确认失败回调函数
ConfirmCallback nackCallback=(deliveryTag,multiple)->{
// 获取确认发布失败的消息
String message=map.get(deliveryTag);
System.out.println(message+"发布失败");
};
// 添加发布确认监听器
channel.addConfirmListener(ackCallback,nackCallback);
for (int i = 0; i < 100; i++) {
String message=i+"";
channel.basicPublish("",RabbitConstant.QUEUE_CONFIRM,null,message.getBytes());
/**
* 将发送的消息存入map
* channel.getNextPublishSeqNo():要发布的下一条消息的序列号
*/
map.put(channel.getNextPublishSeqNo(),message);
}
System.out.println("消息发布完成");
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!