如果有帮助到您,请麻烦帮忙点个赞,谢谢~
保证消息的可靠性
1.生产者层面(两个回调方法)
a.发送方确认机制
spring:
rabbitmq:
publisher-confirm-type: correlated # 开启确认回调 确认发送到交换机
b.发送消息队列确认机制
spring:
rabbitmq:
publisher-returns: true # 开启退回回调
template:
mandatory: true #为true时,消息通过交换器无法匹配到队列会返回给生产者 并触发MessageReturn,为false时,匹配不到会直接被丢弃
/* 配置 */
/**
* 生产者层面消费确认配置类
*/
@Component
public class MQConfigInit implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
@Resource
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
this.rabbitTemplate.setReturnsCallback(this);
this.rabbitTemplate.setConfirmCallback(this);
}
/**
*
* @param correlationData correlationData是发送消息时候携带的消息
* @param ack 如果为true,表示交换机接收到消息了
* @param cause 异常消息
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack){
System.out.println("消息发送到rabbit broker成功");
}else {
System.out.println("消息发送到rabbit broker失败,原因:"+cause);
//TODO ===>做业务处理
}
}
/**
* 当routingkey不存在或错误的时候,会触发该方法
* @param returnedMessage
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息对象===>:" + returnedMessage.getMessage());
System.out.println("错误码===>:" + returnedMessage.getReplyCode());
System.out.println("错误信息===>:" + returnedMessage.getReplyText());
System.out.println("消息使用的交换器===>:" + returnedMessage.getExchange());
System.out.println("消息使用的路由key===>:" + returnedMessage.getRoutingKey());
//TODO ===>做业务处理
// 这里提供一下个人思路:
//1.数据存入数据库,进行人工干预或其他处理
//2.创建一个报警队列,用于存放没有路由到正确队列的数据并做处理
}
}
c.交换机、队列、消息的持久化
默认持久化
2.消费者层面
a.手动ack应答
在保证发送方和 RabbitMQ Server 的消息可靠性的前提下,只需要保证消费者在消费消息时异常消息不丢失即可保证消息的可靠性。
RabbitMQ 提供了 消费者应答机制 来使 RabbitMQ 能够感知到消费者是否消费成功消息,默认情况下,消费者应答机制是自动应答的,也就是RabbitMQ 将消息推送给消费者,便会从队列删除该消息,如果消费者在消费过程失败时,消息就存在丢失的情况。所以需要将消费者应答机制设置为手动应答,只有消费者确认消费成功后才会删除消息,从而避免消息的丢失。
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual // 开启手动确认消费机制
prefetch: 1 // 每个消费者每次可以消费一个
/* 示例 */
@RabbitListener(queues = MQDefinition.TEST_DIRECT_QUEUE1)
public void listen_direct1(Message message, Channel channel) throws IOException {
System.out.println("消费者1收到消息:"+new String(message.getBody()));
try {
//业务处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}catch (Exception e){
// 记录日志
// 这里提供一下个人思路:
//1.数据存入数据库,进行人工干预或其他处理
//2.通过创建一个死信队列并放入异常数据,做重发或其他特殊处理
log.info("出现异常:{}", e.getMessage());
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
}
}
/* ack与nack的参数说明 */
void basicAck(long deliveryTag, boolean multiple) 方法(会抛异常):
deliveryTag:该消息的index
multiple:是否批量处理(true 表示将一次性ack所有小于deliveryTag的消息)
void basicNack(long deliveryTag, boolean multiple, boolean requeue) 方法(会抛异常):
deliveryTag:该消息的index
multiple:是否批量处理(true 表示将一次性ack所有小于deliveryTag的消息)
requeue:被拒绝的是否重新入队列(true 表示添加在队列的末端;false 表示丢弃)
3.springboot提供的消息重试机制
/* 通过配置在消费者的方法上如果执行失败或执行异常只需要抛出异常(一定要出现异常才会触发重试,注意:不要捕获异常) 即可实现消息重试, 这样也可以保证消息的可靠性。 */
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: auto # 开启自动确认消费机制
retry:
enabled: true # 开启消费者失败重试
initial-interval: 5000ms # 初始失败等待时长为5秒
multiplier: 1 # 失败的等待时长倍数(下次等待时长 = multiplier * 上次等待时间)
max-attempts: 3 # 最大重试次数
stateless: true # true无状态;false有状态(如果业务中包含事务,这里改为false)