RabbitMQ消息可靠性(二)-- 消费者消息确认

一、消费者消息确认是什么?

在这种机制下,消费者在接收到消息后,需要向 RabbitMQ 发送确认信息,告知 RabbitMQ 已经接收到该消息,并已经处理完毕。如果 RabbitMQ 没有接收到确认信息,则会将该消息重新加入队列,等待其他消费者继续处理。

消费者消息确认机制能够保证消息不会因为消费者宕机或其他原因而丢失,从而保证了消息的可靠性和稳定性。

RabbitMQ 支持两种消费者消息确认机制:自动确认和手动确认。在自动确认模式下,消费者在接收到消息后,RabbitMQ 会自动将该消息标记为已经确认。在手动确认模式下,消费者需要向 RabbitMQ 显式地发送确认信息,才能完成消息的确认。

二、代码实现

1.修改 application.yml 配置

复制代码
spring:
  rabbitmq:
    listener:
      simple:
        # RabbitMQ开启手动确认
        acknowledge-mode: manual

而SpringAMQP则允许配置三种确认模式:

  1. manual:手动ack,需要在业务代码结束后,调用api发送ack。
  2. auto:自动ack,由spring监测listener代码是否出现异常,没有异常则返回ack;抛出异常则返回nack
  3. none:关闭ack,MQ假定消费者获取消息后会成功处理,因此消息投递后立即被删除

2.消费者确认

生产者发送一笔需要消费的订单到Direct Exchange直连交换机

java 复制代码
    @GetMapping("/sendDirectMessage")
    @ApiOperation(value = "sendDirectMessage")
    @ApiOperationSupport(order = 1)
    public String sendDirectMessage(@RequestParam String orderNo){
        //模拟消费 编号NO1000的订单
        rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", orderNo);
        return "ok";
    }

下面是消费者的处理逻辑

这里的消息序号是系统自动生成的,还需要注意的是,在手动确认模式下,如果消费者在处理消息时发生了异常或错误的时候

需要确保将该消息重新加入队列或者删除队列之后将该信息保存至数据库中记录下来,否则该消息将被认为已经成功处理并确认。因此,在编写消费者代码时,需要谨慎处理异常情况,避免因为异常而导致消息丢失或重复处理等问题。

java 复制代码
/**
 * 消费者,用于消费队列信息
 */
@Component
@Slf4j
public class DirectConsumer {

    @Resource
    RedisService redisService;

    @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
    @RabbitHandler
    public void process(Message message, Channel channel) {
        // 消息序号
        long tag = message.getMessageProperties().getDeliveryTag();
        // 取出订单编码
        String orderNo = new String(message.getBody(), StandardCharsets.UTF_8);
        HashOperations hashOperations = redisService.redisTemplate.opsForHash();
        try {
            //判断该订单是否已经被消费
            if (hashOperations.entries("rabbit-tag").containsKey(orderNo)){
                log.info("订单已经被消费============>{}", orderNo);
                /**
                 * TODO 手动确认消息
                 * tag:消息序号
                 * multiple:消息的标识,是否确认多条,false只确认当前一个消息收到,true确认所有consumer获得的消息(成功消费,消息从队列中删除
                 */
                channel.basicAck(tag, false);
                return;
            }

            //TODO 消费者消费信息(自己的业务逻辑).......

            // 将订单id存入redis,标记该订单消费成功
            hashOperations.put("rabbit-tag", orderNo, "OK");
            channel.basicAck(tag,false);
            log.info("订单消费成功-----> 订单编号{}",orderNo);
        } catch (Exception e) {
            e.printStackTrace();
            try {
                // 将订单id存入redis,标记该订单消费失败
                hashOperations.put("rabbit-tag", orderNo, "NO");
                /**
                 * TODO 消费者消费消息异常,手动否认信息,将消息退回到队列中
                 * tag:消息序号
                 * multiple:消息的标识,是否确认多条,false只确认当前一个消息收到,true确认所有consumer获得的消息(成功消费,消息从队列中删除
                 * requeue:是否要退回到队列
                 */
                channel.basicNack(tag, false, false);
                log.error("订单消费失败-----> 订单编号{}, 原因{}",orderNo, e.getMessage());
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

    }
}
相关推荐
茶杯梦轩4 天前
从零起步学习RabbitMQ || 第三章:RabbitMQ的生产者、Broker、消费者如何保证消息不丢失(可靠性)详解
分布式·后端·面试
回家路上绕了弯6 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
用户8307196840826 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
用户8307196840828 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者9 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者11 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧12 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖12 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农12 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者12 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端