RabbitMQ怎么确保消息可以一定发出去?

文章目录

    • 1.生产者确认
      • 1.1.发送确认机制设置
      • 1.2.消息丢失、非信任或失败
      • 1.3.消息重复消费
      • 1.4.消费成功通知
    • 2.持久化
    • 3.手动Ack
      • 手动确认常用API

RabbitMQ针对这个问题,提供了以下几个机制来解决:

1.生产者确认

1.1.发送确认机制设置

要得到rabbitmq发送消息被接收并且消费成功,必须是设置confirm 模式,即设置信道需要为 confirm 信任模式,则所有在信道上发布的消息都会分配一个唯一 ID(也可以根据业务情况手动分配一个messageID),消息被投递到exchange交换机,再投递给消费者,在消费者消费成功确认后,会给生产者(包含消息唯一ID)返回确认信息,告知生产者,消息消费成功。

1.2.消息丢失、非信任或失败

如果 RabbitMQ发生内部错误从而导致消息丢失,消息消息者就会接收不到消息;如果消息的信息ID为空,会发送一条ack消息给生产者为非信任消息。

1.3.消息重复消费

消息在没有做好控制的情况下,可能会重复消费,所以需要判断是否重复消费

1.4.消费成功通知

消息在被消费成功之后,会告诉rabbitmq,生产者发送的消息,已经被我成功消费了。

2.持久化

  1. 消息发送到 mq 之前先把消息存储到 mysql 中,消息有一个存根避免消息在 mq 中丢失后没地方找这个消息了。
    表设计有几个关键字段:
    messageId:流水 ID,用来在消息的生产端和消费端串联使用,根据这个 id 找到消费端和生产端唯一的消息;
    messageContent:消息内容;
    count:发送到 broker 的次数,如果超过特定次数就不往 broker 发送;
    status:消息状态,已确认、未确认、已作废,如果消费端已经确认就需要修改该状态。

  2. 定时扫描 mysql 中未确认的消息,扫描到后重新发送到 broker,如果 count 超过了特定次数就不发送到 broker,避免消息一直消费失败,大量的消息重复发送到 broker ,导致 broker 被垃圾消息填满。

  3. 消费者消费消息后,如果消费成功则需要回调生产者修改生产者端消息表的状态把状态变成已确认状态,但是这里存在一个问题,回调的时候可能出现问题,导致消息消费成功了,但是状态没改。

  4. 第 3 点这种情况就会存在消息又会被重新投递到 broker 导致消费者重新消费,这就是消息重复消费问题,这时候消费端必须要有幂等设计,在消费端就需要有本地消息表, 当我们消费消息的时候先查询本地消息表记录,如果能根据 messageId 查询到记录则说明之前消费过就不再消费了,如果没有记录则说明该记录还没有处理过就消费该消息。如果消费成功就往消息表中存该条消息。

  5. 如果消费者 ACK 失败,但是业务处理成功,那么消息还会存在在 mq 中的,这也会导致重复消费。

  6. 还有些异常情况,比如消息发送到 mq 成功,插入到本地表中失败,消费者消费成功后插入到本地消息表失败,这些都会存在消息丢失或者重复消费,所以万无一失的做法是生产端和消费端必须要有对账操作,双方对账查找遗漏的业务做人工补偿,人工跑批处理。

3.手动Ack

消费者收到消息之后,等到业务流程走完,确当数据已经落地,自己手动告诉mq服务端我收到消息了,然后服务端再把消息销毁。

如果消费异常,消息不会重新入队列,此时消费的状态为 Unacked , 但是,服务重启之后,消费会被重新发送

手动确认常用API

channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);
ack表示确认消息。multiple:false只确认该delivery_tag的消息,true确认该delivery_tag的所有消息

channel.basicReject(msg.getMessageProperties().getDeliveryTag(),false);
Reject表示拒绝消息。requeue:false表示被拒绝的消息是丢弃;true表示重回队列

channel.basicNack(msg.getMessageProperties().getDeliveryTag(),false,false);
nack表示拒绝消息。multiple表示拒绝指定了delivery_tag的所有未确认的消息,requeue表示不是重回队列

那再来了解一下自动Ack:

自动Ack

不管你数据有没有落地,消费者一旦收到消息就自动ack告诉服务端我收到消息了,然后服务端清理数据。

你可能感兴趣的:(java-rabbitmq,rabbitmq,java)