RabbitMQ(三)-- 消息可靠性
RabbitMQ消息的可靠性投递主要两种实现:
1、通过实现消费的重试机制,通过@Retryable来实现重试,可以设置重试次数和重试频率;
2、生产端实现消息可靠性投递。
两种方法消费端都可能收到重复消息,要求消费端必须实现幂等性消费。
消息的可靠投递
生产端
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式
-
confirm 确认模式
-
return 退回模式
消息投递到exchange的确认模式
rabbitmq的消息投递的过程为:
producer ——> rabbitmq broker cluster ——> exchange ——> queue ——> consumer
-
生产端发送消息到rabbitmq broker cluster后,异步接受从rabbitmq返回的ack确认信息
-
生产端收到返回的ack确认消息后,根据ack是true还是false,调用confirmCallback接口进行处理
1、改yml
spring:
#rabbitmq 连接配置
rabbitmq:
publisher-confirm-type: correlated # 开启confirm确认模式
2、实现confirm方法
实现ConfirmCallback接口中的confirm方法,消息只要被 rabbitmq broker接收到就会触ConfirmCallback 回调,ack为true表示消息发送成功,ack为false表示消息发送失败
package com.rabbitmq.config;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component;
/*** 实现ConfirmCallback接口 */
@Component
public class ConfirmCallbackService implements RabbitTemplate.ConfirmCallback {
/**
* @param correlationData 相关配置信息
* @param ack exchange交换机 是否成功收到了消息。true 成功,false代表失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
//接收成功
System.out.println("成功发送到交换机<===>");
} else {
//接收失败
System.out.println("失败原因:===>" + cause);
//TODO 做一些处理:消息再次发送等等
}
}
}
3、测试
定义 Exchange 和 Queue
定义交换机 confirmTestExchange 和队列 confirm_test_queue ,并将队列绑定在交换机上。
package com.rabbitmq.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/***
*队列与交换机绑定
*/
@Configuration
public class QueueConfig {
@Bean(name = "confirmTestQueue")
public Queue confirmTestQueue() {
return new Queue("confirm_test_queue", true, false, false);
}
@Bean(name = "confirmTestExchange")
public FanoutExchange confirmTestExchange() {
return new FanoutExchange("confirmTestExchange");
}
@Bean
public Binding confirmTestFanoutExchangeAndQueue(
@Qualifier("confirmTestExchange") FanoutExchange confirmTestExchange,
@Qualifier("confirmTestQueue") Queue confirmTestQueue) {
return BindingBuilder.bind(confirmTestQueue).to(confirmTestExchange);
}
}
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitmqApplication.class)
public class Producer {
//注入rabbitmq对象
@Autowired private RabbitTemplate rabbitTemplate;
//注入 ConfirmCallback对象
@Autowired private ConfirmCallbackService confirmCallbackService;
@Test public void test() {
//
rabbitTemplate.setConfirmCallback(confirmCallbackService);
//发送消息
rabbitTemplate.convertAndSend("confirmTestExchange1", "", "hello,ConfirmCallback你好");
}
}
正确情况,ack返回true,表示投递成功。
改变交换机名字,发送到一个不存在的交换机
//发送消息
rabbitTemplate.convertAndSend("confirmTestExchange1", "", "hello,ConfirmCallback你好");
消息未投递到queue的退回模式
消息从 exchange–>queue 投递失败则会返回一个 returnCallback
生产端通过实现ReturnCallback接口,启动消息失败返回,消息路由不到队列时会触发该回调接口
1、改yml
spring:
# rabbitmq 连接配置
rabbitmq: publisher-returns: true # 开启退回模式
2、设置投递失败的模式
如果消息没有路由到Queue,则丢弃消息(默认)
如果消息没有路由到Queue,返回给消息发送方ReturnCallBack(开启后)
rabbitTemplate.setMandatory(true);
3、实现returnedMessage方法
启动消息失败返回,消息路由不到队列时会触发该回调接口
package com.rabbitmq.config;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
@Component
public class ReturnCallbackService implements RabbitTemplate.ReturnCallback {
/** @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("消息对象===>:" + message);
System.out.println("错误码===>:" + replyCode);
System.out.println("错误信息===>:" + replyText);
System.out.println("消息使用的交换器===>:" + exchange);
System.out.println("消息使用的路由key===>:" + routingKey);
//TODO ===>做业务处理
}
}
4、测试
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitmqApplication.class)
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
//注入rabbitmq对象
@Autowired
private ConfirmCallbackService confirmCallbackService;
@Autowired
private ReturnCallbackService returnCallbackService;
@Test
public void test() {
/**
* 确保消息发送失败后可以重新返回到队列中
*/
rabbitTemplate.setMandatory(true);
/**
* 消息投递到队列失败回调处理
*/
rabbitTemplate.setReturnCallback(returnCallbackService);
/**
* 消息投递确认模式
*/
rabbitTemplate.setConfirmCallback(confirmCallbackService);
//发送消息
rabbitTemplate.convertAndSend("confirmTestExchange", "info", "hello,ConfirmCallback你好");
}
}
如果不存在路由key,会调用ReturnCallback接口
消费端
消息确认机制ack
ack指Acknowledge确认。 表示消费端收到消息后的确认方式
消费端消息的确认分为:自动确认(默认)、手动确认、不确认
-
AcknowledgeMode.NONE:不确认
-
AcknowledgeMode.AUTO:自动确认
-
AcknowledgeMode.MANUAL:手动确认
其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从RabbitMQ 的消息 缓存中移除。
但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
1、yml
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual # 手动确认
2、确认配置
@Component
@RabbitListener(queues = "confirm_test_queue")
public class ReceiverMessage {
@RabbitHandler
public void processHandler(String msg, Channel channel, Message message) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println("消息内容===>" + new String(message.getBody()));
//TODO 具体业务逻辑
//手动签收[参数1:消息投递序号,参数2:批量签收]
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
//拒绝签收[参数1:消息投递序号,参数2:批量拒绝,参数3:是否重新加入队列]
channel.basicNack(deliveryTag, true, true);
}
}
}
channel.basicNack 方法与 channel.basicReject 方法区别在于basicNack可以批量拒绝多条消息,而basicReject一次只能拒绝一条消息。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南