参考:https://blog.csdn.net/weixin_34367257/article/details/91849513
https://www.cnblogs.com/aijiaxiang/p/12803591.html
参考:https://www.cnblogs.com/yuefeng123/p/12658531.html
参考:https://blog.csdn.net/wufaliang003/article/details/108314339
一、发送消息的过程
发送消息(发送端- 交换机- 队列) (实际是2个过程)
总结:只有当消息确认ack为true且未执行失败回调方法时,才保证发送端消息已经成功发送!
执行失败的回调方法时,说明消息已经发送到(broker)交换机(第一个过程是OK的),交换机路由到队列出现了问题(第二个过程)。此时需要我们自己来解决:
发送端信息成功到交换机确认机制:
前提:开启消息确认
connectionFactory.setPublisherConfirms(true);
如果成功,会给发送端一个为true的ack,表示发送成功!
如果失败,会给发送端一个为false的ack,表示发送失败!
代码如下:
/**
* 发送端是否发送到交换机
*/
public class RabbitmqConfirm implements RabbitTemplate.ConfirmCallback {
/**
* 发送端回调方法
* @param correlationData 发送到mq时传入的业务参数(例如:业务id)
* @param ack 结果标志
* true: 发送端 ——> 交换机 成功
* false:发送端 ——> 交换机 失败
* @param message 结果消息:ack为true时,返回为null
* ack为false时,返回失败消息
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String message) {
System.out.println(correlationData.getId());
System.out.println(ack);
System.out.println(message);
}
}
交换机信息成功路由到队列确认机制:
前提:开启消息确认
connectionFactory.setPublisherConfirms(true);
开启失败回调
rabbitTemplate.setMandatory(true);
如果成功,则不会调用失败回调方法!
如果失败,则会调用失败回调方法!
代码如下:
/**
* 交换机是否发送到队列
*/
public class RabbitmqReturnConfirm implements RabbitTemplate.ReturnCallback {
/**
* 交换机发送队列失败回调:
*
* 采用一个不存在的routingKey可做测试
* @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(routingKey);
}
}
二、Message acknowledgment 消息确认机制
//消费者成功消费消息后,给Rabbitmq服务器一个应答
默认情况下,RabbitMQ 会顺序的分发每个Message。当分发后,会将该Message删除,然后将下一个Message分发到下一个Consumer。这种分发方式叫做round-robin。
每个Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中,Consumer出错了,异常退出了,而数据还没有处理完成,那么非常不幸,这段数据就丢失了。因为我们采用no-ack的方式进行确认,也就是说,每次Consumer接到数据后,而不管是否处理完成,RabbitMQ Server会立即把这个Message标记为完成,然后从queue中删除了。
如果一个Consumer异常退出了,它处理的数据能够被另外的Consumer处理,这样数据在这种情况下就不会丢失了(注意是这种情况下)。
为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack。而应该是在处理完数据后发送ack。
在处理数据后发送的ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以去安全的删除它了。
如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer。这样就保证了在Consumer异常退出的情况下数据也不会丢失。
这里并没有用到超时机制。RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有被正确处理。也就是说,RabbitMQ给了Consumer足够长的时间来做数据处理。
消息确认,对于spring-boot来说,就是一个开关,它就是spring.rabbitmq.listener.simple.acknowledge-mode
acknowledgeMode有三值:
-
A、
NONE
= no acks will be sent (incompatible with channelTransacted=true).
RabbitMQ calls this “autoack” because the broker assumes all messages are acked without any action from the consumer. -
B、
MANUAL
= the listener must acknowledge all messages by calling Channel.basicAck(). -
C、
AUTO
= the container will acknowledge the message automatically, unless the MessageListener throws an exception.
Note that acknowledgeMode is complementary to channelTransacted - if the channel is transacted then the broker requires a commit notification in addition to the ack. This is the default mode. See also txSize.
(注意,acknowledgement emode是对channelTransacted的补充——如果通道被处理了,那么除了ack,代理还需要一个commit通知。这是默认模式。也看到txSize。)