rabbitmq系列03---发布确认
一、发布确认逻辑
生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID (从 1 开始),一旦消息被投递到所有匹配的队列之后,broker 就会发送一个确认给生产者 (包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了
当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息, 生产者应用程序同样可以在回调方法中处理该 nack 消息。
二、发布发布确认的策略
发布确认默认是没有开启的
,如果要开启,需要调用方法 confirmSelect
,每当你要想使用发布确认,都需要在 channel 上调用该方法
1、单个发布确认
这是一种简单的确认方式,它是一种同步确认发布的方式,也就是发布一个消息之后只有它被确认发布,后续的消息才能继续发布,waitForConfirmsOrDie(long) 这个方法只有在消息被确认的时候才返回,如果在指定时间范围内这个消息没有被确认那么它将抛出异常。
这种确认方式有一个最大的缺点就是:发布速度特别的慢,因为如果没有确认发布的消息就会阻塞所有后续消息的发布,这种方式最多提供每秒不超过数百条发布消息的吞吐量。当然对于某些应用程序来说这可能已经足够了。
//单个确认 public static void publishMessageSingle() throws Exception{ Channel channel = Util.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"); }
2、批量确认发布
与单个等待确认消息相比,先发布一批消息然后一起确认可以极大地提高吞吐量,当然这种方式的缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布消息。当然这种方案仍然是同步的,也一样阻塞消息的发布。
//批量确认 public static void publishMessageBatch() throws Exception{ Channel channel = Util.getChannel(); //队列的声明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName,true,false,false,null); //开启发布确认 channel.confirmSelect(); //批量确认消息大小 int batchSize = 100; //开始时间 long begin = System.currentTimeMillis(); //批量发消息 批量发布确认 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + ""; channel.basicPublish("",queueName,null,message.getBytes()); //每达到100次,批量发布确认一次 if(i%batchSize == 0){ //发布确认 channel.waitForConfirms(); } } //结束时间 long end = System.currentTimeMillis(); System.out.println("发布"+MESSAGE_COUNT+"个批量确认消息,耗时"+(end-begin)+"ms"); }
3、异步确认发布
异步确认虽然编程逻辑比上两个要复杂,但是性价比最高,无论是可靠性还是效率都没得说,他是利用回调函数来达到消息可靠性传递的,这个中间件也是通过函数回调来保证是否投递成功。
//异步发布确认 public static void publishMessageAsync() throws Exception{ Channel channel = Util.getChannel(); //队列的声明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName,true,false,false,null); //开启发布确认 channel.confirmSelect(); //开始时间 long begin = System.currentTimeMillis(); /** * deliveryTag:消息的标记 * multiple:是否批量确认 */ //消息确认成功 回调函数 ConfirmCallback ackCallback = (deliveryTag,multiple)->{ System.out.println("确确认消息"+deliveryTag); }; //消息确认失败 回调函数 ConfirmCallback nackCallback = (deliveryTag,multiple)->{ System.out.println("未确认消息"+deliveryTag); }; //准备消息监听器 监听那些消息成功,那些消息失败 /** * 1.监听成功方法 * 2.监听失败方法 */ channel.addConfirmListener(ackCallback,nackCallback);//异步通知 //批量发送消息 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + ""; channel.basicPublish("",queueName,null,message.getBytes()); } //结束时间 long end = System.currentTimeMillis(); System.out.println("发布"+MESSAGE_COUNT+"个异步发布确认消息,耗时"+(end-begin)+"ms"); }