RabbitMQ报错:Shutdown Signal channel error; protocol method

报错信息:

Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80)

原因

默认情况下 RabbitMQ 是自动ACK(确认签收)机制,就意味着 MQ 会在消息发送完毕后,自动帮我们去ACK(确认),若是在代码中再手动确认签收,就会造成确认错误。

"PRECONDITION_FAILED - unknown delivery tag" 表明交付标签(delivery tag)不合法或已经被确认过

因此我们需要在订阅者(消费者)的方法上标识,消息手动确认签收ackMode = "MANUAL",代码如下:

复制代码
    @RabbitListener(queues = RabbitMqContants.DIRECT_QUEUE1_NAME,ackMode = "MANUAL")
    public void directConsumerA(String msg, Channel channel, Message message) throws IOException {

        try {
            // 处理消息
            log.info("=================DIRECT-队列1-消费者A 接收到消息:msg = {}", msg);
            //消息消费手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
            // 处理异常,可能需要重新将消息放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }

关于rabbitMQ在yml配置手动ack不生效,重复消费的问题

复制代码
spring:
 rabbitmq:
    host: xxx
    port: 5672
    username: guest
    password: guest
    listener:
      direct:
        acknowledge-mode: manual # 手动ACK开启:自动确认 none ;手动确认 manual ;根据异常情况确认 auto (较麻烦,不推荐)
此处已经设置了手动答应,消费者代码是这样的:
复制代码
@RabbitListener(queues = RabbitMqContants.DIRECT_QUEUE1_NAME)
    public void directConsumerA(String msg, Channel channel, Message message) throws IOException {

        try {
            // 处理消息
            log.info("=================DIRECT-队列1-消费者A 接收到消息:msg = {}", msg);
            //消息消费手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
            // 处理异常,可能需要重新将消息放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
然而消费者会重复消费消息,控制台还是会打印如下异常:
复制代码
Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80)

查找资料和测试发现,rabbitmq默认是用的SimpleRabbitListenerContainerFactory

配置文件中如果不指定监听容器类型的话,配置是不会生效的,

只要配置文件配置改成以下配置,这个问题就可以解决了。

复制代码
spring:
 rabbitmq:
    host: xxx
    port: 5672
    username: guest
    password: guest
    listener:
      # 设置监听容器(Listener container)类型,如不设置,将会默认为SimpleRabbitListenerContainerFactory,且下面的direct配置不生效
      type: direct
      direct:
        acknowledge-mode: manual # 手动ACK开启:自动确认 none ;手动确认 manual ;根据异常情况确认 auto (较麻烦,不推荐)
重点是type这个配置,如果不设置type的话,要将direct改成simple才会生效

总结

详情请参考:
RabbitMQ报错:Shutdown Signal: channel error; protocol method
关于rabbitMQ在yml配置手动ack不生效,重复答应的问题

相关推荐
用户8307196840821 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
用户8307196840822 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者3 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者5 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧6 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖6 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农6 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者6 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀6 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式
Ronin3056 天前
信道管理模块和异步线程模块
开发语言·c++·rabbitmq·异步线程·信道管理