作者处于学习阶段,刚刚完成RabbitMQ的学习,作为学生,我会用更通俗的说法,来叙述自己对RabbitMQ的了解。愿各位大佬看到有见解错误的地方和叙述不好的地方,能够帮忙纠正。来帮助大家更加深入的了解RabbitMQ。
-
发布确认原理
生产者将信道设置为confirm模式, 信道上发布的每一条消息都会携带唯一的ID。当消息成功到达队列后,队列返回给生产者一个确认,让生产者知道消息被接受到了。如果消息和队列设置了持久化(上一节有提到过,链接:RabbitMQ学习总结(五)之消息应答和持久化),那么发布成功的消息将会先存储到磁盘,然后再发送给生产者。
confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信
道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调
方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消
息,生产者应用程序同样可以在回调方法中处理该 nack 消息。 -
如何开启发布确认
在channel上调用congirmSelect()方法,便可以开启发布确认。
-
单个确认发布
简单来说就是单个确认,一个一个的确认,当确认一条消息后,才能继续后续消息的发布。缺点:速度慢,若出现一条消息没有确认便会堵塞后续消息。
对应代码:`//单个确认
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("成功"); }else { System.out.println("失败"); } } //结束时间 long end = System.currentTimeMillis(); System.out.println((end-begin)+"ms");
}`
-
批量确认发布
由名知意,便是多条一起确认。当接收多条后,再发布确认。但是当出现问题时,不知道具体是哪条消息,而且当出现问题后,后续的消息同样阻塞。
对应代码:`public static void publishMessageBatch() 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()); if((i+1)%batchSize==0){ //发布确认 channel.waitForConfirms(); System.out.println("确认成功"); } } //结束时间 long end = System.currentTimeMillis(); System.out.println((end-begin)+"ms");
}`
-
异步确认发布
在学习时,看了很多网上的知识,好多只是把图片放在了上面,并没有进行讲解,只不过是原搬原抄。我将我的理解在下总结,如有问题,请多指正。
生产者将消息发送到队列中,队列将收到的消息和未收到的消息全部放入到回调函数中,生产者调用回调函数便可以知道哪个消息发送成功,哪个消息出现问题。
对应代码:`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> outstandingComfirms = new ConcurrentSkipListMap<>(); //开始时间 long begin = System.currentTimeMillis(); //消息确认成功 回调函数 ConfirmCallback ackCallback = (deliveryTag, multiple) -> { if (multiple){ //2.删除已经确认的消息 剩下的就是确认失败的消息 ConcurrentNavigableMap<Long, String> confrmed = outstandingComfirms.headMap(deliveryTag); confrmed.clear(); }else{ outstandingComfirms.remove(deliveryTag); } System.out.println("确认的消息内容"+deliveryTag); }; //消息确认失败 回调函数 /** * 1.消息的标记 * 2.是否为批量确认 */ ConfirmCallback nackCallback = (deliveryTag, multiple) -> { String message = outstandingComfirms.get(deliveryTag); System.out.println("未确认的消息是:"+message+"未确认的消息" + deliveryTag); };; //准备消息的监听器 监听哪些消息成功了 哪些消息消失了 channel.addConfirmListener(ackCallback,nackCallback); //批量发送消息 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = "消息"+i; channel.basicPublish("",queueName,null,message.getBytes()); //1:此处记录下所有要发送的消息 消息的总和 outstandingComfirms.put(channel.getNextPublishSeqNo(),message); } //结束时间 long end = System.currentTimeMillis(); System.out.println((end-begin)+"ms");
}`