消息丢失怎么办

3种情况,mq消息丢失做集群开启持久化durable

如何可靠消费 开启ack重试机制一般重试3-5次,不行的话就把这个消息打到死信队列,死信队列不行再打到重定向队列,重定向不行就报警存到数据库或者缓存中报警人工进行处理.

wi消息队列服务_rabbitmq


wi消息队列服务_队列_02


wi消息队列服务_分布式_03


第一个问题:如和保证消费者能够一定收到消息:

  • 做消息的持久队列(Queue)
  • rabbitmq做集群

第二个问题:如果保证消息的可靠生产(rabbitmq全部挂了)

  • 做消息的备份(冗余)状态0,当rabbitmq服务器好的时候,那么接下类开一个定时器(quartz)间隔一段时间(时间一般通过业务来决定)。
  • 状态如何修改呢?会通过rabbitmq的ack机制来完成confirm,在生产者这边吧消息的状态修改1。

第三种情况:消费报异常了,造成消费无法确认。

  • 会造成死循环
  • 手动ACK + try/catch
  • 如果出现错误做法把消息放入死信队列
  • 如果死信队列还报错,会死信队列绑定一个重定向队列,在发送消息去消费。
  • 如果订单想队列还报错。那么只能把消息打入存储库(mysql,redis,mongodb等) +
  • 报警(发短信) + 人工介入
  • 重试机制,但是会造成消息丢失

第四问题:幂等性问题

  • 因为用户可能重复提交订单
  • 把订单编号设置redis的key
  • 如果key存在,订单已经生成了
  • 如果不存在,订单在生产中。

    在这里插入代码片
/**
 * 消息生产者
 */
@Component
public class Product implements RabbitTemplate.ConfirmCallback{

    private RabbitTemplate rabbitTemplate;

    @Autowired
    public Product(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setConfirmCallback(this);
    }


    /**
     * 生产者发送消息
     * @param msg
     */
    public void sendMsg(String msg) {
        System.out.println("生产者发送消息 : " + msg);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        System.out.println("生产者生成确认接到消息的 UUID: " + correlationData.getId());
        this.rabbitTemplate.convertAndSend("topicExchange", "topic.itcast", msg, correlationData);
    }


    /**
     * 回调的方法
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("确认回调的UUID: " + correlationData.getId() + ",是否收到消息=" + ack + "   ,  原因:" + cause);
    }
}

wi消息队列服务_队列_04

wi消息队列服务_队列_05

@Component
@RabbitListener(queues = "itcast")
public class Consumer {

    @RabbitHandler
    public void con1(String msg, Channel channel, Message message) throws IOException {
        try {
            System.out.println("itcast 消费者: "+msg);
            //消息确认接收
            /**
             * 参数一:message.getMessageProperties().getDeliveryTag()   当前消息索引号
             * 参数二:是否通知全部queue
             */
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            //丢弃这条消息
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
            e.printStackTrace();
        }
    }
}

个人理解:
生产者发消息之后再confirm方法里面判断根据返回的布尔值进行判断,如果是false你就将这条数据存到id数据库中,给个状态status 为0,然后写个定时任务去查状态为0的消息表中的数据,可以重新进行投递,也可以直接调接口要干啥,消费者这边只需要调用channel.basciAck 告知mq 丢弃这条消息不用发了,如果发现被捕获了也是丢弃消息重新发送