一:消息确认种类

RabbitMQ的消息确认有两种。

一种是消息发送确认。这种是用来确认生产者将消息发送给交换器,交换器传递给队列的过程中,消息是否成功投递。发送确认分为两步,一是确认是否到达交换器,二是确认是否到达队列。

第二种是消费接收确认。这种是确认消费者是否成功消费了队列中的消息。

具体建议参考:

这里我们重点研究下接收确认的情况。

为什么接收的确认这么关键呢?这里面有2个难题:1、如何防止消息丢失或未被成功处理;2、如何防止消息被重复消费(尤其是在消费者服务集群的情况下);

第一个问题可以这样处理:

1、在启动MQ侦听服务时配置这个参数:

acknowledge-mode :manual

可以确保每个消息是需要消费者手工确认的。

2、在真正接收到消息时,先不回复确认,而是先处理业务逻辑,处理成功后回复确认,此时MQ会将该消息删除;业务逻辑处理失败则回复拒绝,然后将消息登记到异常表,有专门的异常处理程序处理。

参考代码如下:

 

配置文件: 

rabbitmq:
  host: 192.168.1.203
  port: 5672
  username: admin
  password: admin
  publisher-confirms: true    #  消息发送到交换机确认机制,是否确认回调
  listener:
    acknowledge-mode :manual   # 每条消息必须手工确认,具体参考附录1

MQ接收的服务写法:

@Component
public class FirstConsumer1 {
    @Autowired
    private FirstSender firstSender;
    @RabbitListener(queues="test_testQueue" )
    public void handleMessage(Message message ,Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
        channel.basicAck(tag, false);   //手动确认消息已收到
        String  recvStr = new String(message.getBody());
        // 处理消息
        System.out.println("得到消息为"+recvStr);
        
        //根据业务逻辑登记判重表,在业务层面避免消息被二次消费

    }
}

 

 

 

第二个问题相对简单,因为每个消息有唯一编号,因此在消费者端可以从业务层面建立判重表,来避免重复消费的问题。

 

为了保证消息从队列可靠的到达消费者,RabbitMQ提供了消息 确认机制,消费者在订阅队列的时候,可以指定autoAck参数,当autoACK等于false时,RabbitMQ会等待显示的恢复确认信号之后才从内存或者磁盘中移除消息(实质上是先打上删除标记,之后再删除)

       当autoAck属性为true的时候,RabbitMQ会自动把发送出去的消息标记为确认,然后从内存或者磁盘中移除,而不管消费者有没有收到消息。

      采用了这个机制后不用担心消费者接受不到消息的问题因为,消费者就算是挂了,消息会一直存在,消费者服务启动的时候消息就会自动的进行推送,因为RabbitMQ会一直等待持有消息直到消费者显示的调用Basic.ack命令为止。

       当开启消息确认机制以后,对于rabbitmq服务端而言,队列中的消息分成了两个部分,一部分是等待投递给消费者的消息;一部分是已经投递给消费者的消息。如果RabbitMQ一直没有收到消费者的确认信号,并且消费者此消息的消费者已经断开连接,则RabbitMQ会安排重新入队列,等待投递给下一个消费者,当然也有可能是原来的消费者。

     RabbitMQ除了提供了消息确认机制以外,在2.0以后的版本引入了Basic.Reject这个命令,意思是明确拒绝当前消息而不是确认。

单独的给一个消息接收者设置ack。

 

附录1:

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.