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();
            }
        }

    }
}
相关推荐
三不原则2 小时前
Kafka入门指南:从安装到集群部署
分布式·kafka
拾忆,想起5 小时前
Redis红锁(RedLock)解密:分布式锁的高可用终极方案
java·数据库·redis·分布式·缓存·性能优化·wpf
小白学大数据8 小时前
Scrapy 框架实战:构建高效的快看漫画分布式爬虫
开发语言·分布式·爬虫·python·scrapy
项目題供诗9 小时前
Hadoop(六)
大数据·hadoop·分布式
Hello.Reader13 小时前
Kafka 4.0 五大 API 选型指南、依赖坐标、上手示例与最佳实践
分布式·kafka·linq
鼠鼠我捏,要死了捏14 小时前
Kafka、RabbitMQ 与 RocketMQ 在高并发场景下的高可用与性能对比分析
kafka·rabbitmq·rocketmq
青云交19 小时前
Java 大视界 -- 基于 Java 的大数据实时流处理在智能电网分布式电源接入与电力系统稳定性维护中的应用(404)
java·大数据·分布式·智能电网·flink 实时流处理·kafka 数据采集·iec 61850 协议
Monkey-旭1 天前
鸿蒙 5.1 深度解析:ArkUI 4.1 升级与分布式开发新范式
分布式·wpf·harmonyos·arkts·openharmony·arkui
猿java1 天前
分布式和微服务,它们有什么区别?该如何选择?
分布式·微服务·架构
hello 早上好1 天前
基于 Spring AMQP 的 RabbitMQ 分布式消息系统实战
分布式·spring·java-rabbitmq