Broker: 简单来说就是消息队列服务器实体
Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来
Routing Key: 路由关键字,exchange根据这个关键字进行消息投递
VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的
queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到
vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的
手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。
Producer: 消息生产者,就是投递消息的程序
Consumer: 消息消费者,就是接受消息的程序
Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一
个会话任务
生产者Producer 将消息发送到指定的 交换机Exchange,交换机根据路由规则路由到绑定的 队列Queue 中,最后和消费者建立连接后,将消息推送给 消费者Consumer。
1.消息丢失场景
- 生产者生产消息到RabbitMQ Server消息丢失
- Exchange到Queue消息丢失
- RabbitMQ Server到消费者消息丢失
- RabbitMQ Server存储的消息丢失
2.生产者生产消息到RabbitMQ Server消息丢失
方案分别是 事务机制 和 发送确认机制
事务机制
-
配置事务管理器
@Configuration public class RabbitMQConfig { /** * 配置事务管理器 */ @Bean public RabbitTransactionManager transactionManager(ConnectionFactory connectionFactory) { return new RabbitTransactionManager(connectionFactory); } }
-
添加事务注解开启事务实现事务机制
@Service public class RabbitMQServiceImpl { @Autowired private RabbitTemplate rabbitTemplate; // 事务注解 @Transactional public void sendMessage() { // 开启事务 rabbitTemplate.setChannelTransacted(true); // 发送消息 rabbitTemplate.convertAndSend(RabbitMQConfig.Direct_Exchange, routingKey, message); } }
执行流程为:生产者发送消息之前,开启事务,如果消息发送至 RabbitMQ Server 失败后,进行事务回滚,重新发送。如果 RabbitMQ Server 接收到消息,则提交事务。
缺点:存在阻塞生产者的情况直到 RabbitMQ Server 应答,会降低发送消息的性能,所以一般不会使用事务机制来保证生产者的消息可靠性。
发送确认机制
-
配置文件
spring: rabbitmq: publisher-confirm-type: correlated # 开启发送方确认机制
配置属性:
none
:表示禁用发送方确认机制correlated
:表示开启发送方确认机制simple
:表示开启发送方确认机制,并支持waitForConfirms()
和waitForConfirmsOrDie()
的调用。
一般使用
correlated
开启发送方确认机制。 -
通过调用
setConfirmCallback()
实现异步 confirm 模式感知消息发送结果@Service public class RabbitMQServiceImpl { @Autowired private RabbitTemplate rabbitTemplate; @Override public void sendMessage() { // 发送消息 rabbitTemplate.convertAndSend(RabbitMQConfig.Direct_Exchange, routingKey, message); // 设置消息确认回调方法 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { /** * MQ确认回调方法 * @param correlationData 消息的唯一标识 * @param ack 消息是否成功收到 * @param cause 失败原因 */ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { // 记录日志 log.info("ConfirmCallback...correlationData["+correlationData+"]==>ack:["+ack+"]==>cause:["+cause+"]"); if (!ack) { // 出错处理 ... } } }); } }
生产者发送消息后通过调用 setConfirmCallback()
可以将信道设置为 confirm 模式,所有消息会被指派一个消息唯一标识,当消息被发送到 RabbitMQ Server 后,Server 确认消息后生产者会回调设置的方法,从而实现生产者可以感知到消息是否正确无误的投递,从而实现发送方确认机制。并且该模式是异步的,发送消息的吞吐量会得到很大提升。
3.Exchange到Queue消息丢失
-
配置事务管理器
spring: rabbitmq: publisher-confirm-type: correlated # 开启发送方确认机制 publisher-returns: true # 开启消息返回 template: mandatory: true # 消息投递失败返回客户端
- 通过调用
setReturnCallback()
方法设置路由失败后的回调方法@Service public class RabbitMQServiceImpl { @Autowired private RabbitTemplate rabbitTemplate; @Override public void sendMessage() { // 发送消息 rabbitTemplate.convertAndSend(RabbitMQConfig.Direct_Exchange, routingKey, message); // 设置消息确认回调方法 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { /** * MQ确认回调方法 * @param correlationData 消息的唯一标识 * @param ack 消息是否成功收到 * @param cause 失败原因 */ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { // 记录日志 log.info("ConfirmCallback...correlationData["+correlationData+"]==>ack:["+ack+"]==>cause:["+cause+"]"); if (!ack) { // 出错处理 ... } } }); // 设置路由失败回调方法 rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() { /** * MQ没有将消息投递给指定的队列回调方法 * @param message 投递失败的消息详细信息 * @param replyCode 回复的状态码 * @param replyText 回复的文本内容 * @param exchange 消息发给哪个交换机 * @param routingKey 消息用哪个路邮键 */ @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { // 记录日志 log.info("Fail Message["+message+"]==>replyCode["+replyCode+"]" +"==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]"); // 出错处理 ... } }); } }
通过调用 setReturnCallback()
方法即可实现当交换机路由到指定队列失败后回调方法,拿到被退回的消息信息,进行相应的处理如记录日志或重传等等。
4.RabbitMQ Server到消费者消息丢失
RabbitMQ 提供了 消费者应答机制 来使 RabbitMQ 能够感知到消费者是否消费成功消息,默认情况下,消费者应答机制是自动应答。
-
配置事务管理器
spring: rabbitmq: publisher-confirm-type: correlated # 开启发送方确认机制 publisher-returns: true # 开启消息返回 template: mandatory: true # 消息投递失败返回客户端 listener: simple: acknowledge-mode: manual # 开启手动确认消费机制
-
调用
channel.basicAck()
与channel.basicNack()
执行相应业务@RabbitListener(queues = RabbitMQConfig.MESSAGE_QUEUE) public void onMessage(Message message, Channel channel) { // 获取消息索引 long index = message.getMessageProperties().getDeliveryTag(); // 解析消息 byte[] body = message.getBody(); ... try { // 业务处理 ... ... ... /** * @param deliveryTag:消息的index * @param multiple:是否批量处理(true 表示将一次性ack所有小于deliveryTag的消息) */ // 业务执行成功则手动确认 channel.basicAck(index, false); }catch (Exception e) { // 记录日志 log.info("出现异常:{}", e.getMessage()); try { /** * @param deliveryTag:消息的index * @param multiple:是否批量处理 * @param requeue:被拒绝的是否重新入队列(true 表示添加在队列的末端) */ // 手动丢弃信息 channel.basicNack(index, false, false); } catch (IOException ex) { log.info("丢弃消息异常"); } } }
5.RabbitMQ Server存储的消息丢失
通过对消息的持久化,在发送消息时将消息持久化,并且在创建交换机和队列时也保证持久化。
-
队列和交换机持久化
/** * 消息队列 */ @Bean public Queue queue() { // 参数:name(队列名)、durable(持久化)、 exclusive(独占)、autoDelete(自动删除) return new Queue(MESSAGE_QUEUE, true); } /** * 交换机 */ @Bean public DirectExchange exchange() { // 参数:name(交换机名)、durable(持久化)、autoDelete(自动删除)、arguments(额外参数) return new DirectExchange(Direct_Exchange, true, false); }
- 消息持久化
@Override public void sendMessage() { // 构造消息(将消息持久化) Message message = MessageBuilder.withBody("消息内容".getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build(); // 向MQ发送消息(消息内容都为消息表记录的id) rabbitTemplate.convertAndSend(RabbitMQConfig.Direct_Exchange, routingKey, message); }
通过 setDeliveryMode(MessageDeliveryMode.PERSISTENT)
在构造消息时设置消息持久化。