废话不多说,直入主题

1.怎么才算保证消息的可靠性

java rabbitmq 保证消费端接收到消息_持久化


这个图相信都已经看过无数遍了,从图上我们可以看出来只要保证以下四个节点可靠,那么整个消息就是可靠的

  • 生产者发送的消息保证到达了MQ
  • MQ收到消息保证分发到了Exchange交换机
  • Exchange交换机分发消息到Queue并保证消息的持久性
  • 消费者收到消息之后保证消费正常完成

接下来我们逐个分析每个节点情况可能出现的问题以及解决方案

2.生产者发送消息到MQ失败

我们的生产者发送消息之后可能由于网络闪断等各种原因导致我们的消息并没有发送到MQ之中,但是这个时候我们生产端又不知道我们的消息没有发出去,这就会造成消息的丢失。

解决方案:RabbiMQ引入事务机制和发送方确认机制,事务机制较为耗费性能,选择使用发送方确认机制。
意思就是生产者发送消息到MQ后,MQ会回复一个确认收到的消息给生产者。

具体过程:

# 配置打开消息确认
spring:
  rabbitmq:
    addresses: 127.0.0.1
    host: 5672
    username: guest
    password: guest
    virtual-host: /
    #确认消息已发送到交换机
    spring:
      rabbitmq:
        publisher-confirms: true
    #确认消息已发送到队列
    spring:
      rabbitmq:
        publisher-returns: true

生产者

//在RabbitConfig中加入配置
@Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        /*设置开启Mandatory才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数*/
        rabbitTemplate.setMandatory(true);

        /*消息发送到Exchange的回调,无论成功与否*/
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            log.info("ConfirmCallback:" + "相关数据:" + correlationData);
            log.info("ConfirmCallback:" + "确认情况:" + ack);
            log.info("ConfirmCallback:" + "原因:" + cause);
        });

        /*消息从Exchange路由到Queue失败的回调*/
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.info("ReturnCallback:" + "消息:" + message);
            log.info("ReturnCallback:" + "回应码:" + replyCode);
            log.info("ReturnCallback:" + "回应信息:" + replyText);
            log.info("ReturnCallback:" + "交换机:" + exchange);
            log.info("ReturnCallback:" + "路由键:" + routingKey);
        });
        return rabbitTemplate;
    }

生产者代码里我们看到又多了一个参数:CorrelationData,这个参数是用来做消息的唯一标识,同时我们打开消息确认之后需要对rabbitTemplate多设置一个setConfirmCallback,参数是一个匿名类,我们消息确认成功or失败之后的处理就是写在这个匿名类里面。

我们可以根据ack的状态在回调中处理消息失败的逻辑

消息入队之后MQ宕机出现消息丢失

首先这种情况概率很小,对于这种问题,我们要做的就是对消息持久化,以便于MQ重启之后可以恢复消息

在做消息持久化的时候,我们不仅要做消息持久化,而且要做队列和交换机的持久化。

3.消费者无法正常消费消息

解决方案:打开手动消息确认

spring:
  rabbitmq:
    addresses: 127.0.0.1
    host: 5672
    username: guest
    password: guest
    virtual-host: /
    # 手动确认消息
    listener:
      simple:
          acknowledge-mode: manual

打开手动消息确认之后,只要我们这条消息没有成功消费,无论中间是出现消费者宕机还是代码异常,只要连接断开之后这条信息还没有被消费那么这条消息就会被重新放入队列再次被消费。

在消费过程中可能会产生重复消费的问题,所以我们要保证消息的幂等性,不做详细描述如何保证幂等性(使用检验消息中的唯一ID来保证一条消费只消费一次)

4.常用的成熟解决方案

java rabbitmq 保证消费端接收到消息_持久化_02

大致流程:
1.首先消息落库,存储消息信息和消息的消费状态
2.生产者从DB中拿取数据包装后将消息投递到MQ
3.MQ获取消息确认后交给消费者消费
4.消费者完成消息后修改消息的消费状态
5.开启定时任务,间隔不断刷库,将消费失败的有问题的消息重新投递,俗称兜底操作

注:在整个流程中如果出现问题的话就不往下进行,修改DB中消息的消费状态等待下一次投递消费。