operation basic.ack caused a channel exception precondition_failed: unknown delivery tag 1

文章描述了一个在使用RabbitMQ时遇到的问题,即在手动确认消息消费时,日志中出现unknowndeliverytag错误。通过检查配置发现,由于`SimpleRabbitListenerContainerFactory`默认设置为自动签收,导致手动确认模式失效。解决方法是更改配置为手动acknowledge模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

rabbitmq消费手动确认时rabbitmq日志报 unknown delivery tag 1,代码日志报
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)
在这里插入图片描述
在这里插入图片描述

代码

@Configuration
@Slf4j
public class RabbitConfig {
    // 定义死信队列
    @Bean
    public Queue deadLetterQueue() {
        return new Queue("deadLetterQueue");
    }

    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("deadLetterExchange");
    }
    // 绑定死信队列和死信交换机
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with("deadLetterKey");
    }

    @Bean
    public Queue illegalAlarmQueue() {
        return QueueBuilder.durable("illegalAlarmQueue")
                .withArgument("x-dead-letter-exchange", "deadLetterExchange")
                .withArgument("x-dead-letter-routing-key", "deadLetterKey")
                .build();
    }

    @Bean
    public DirectExchange masterExchange() {
        return new DirectExchange("masterExchange");
    }

    @Bean
    public Binding illegalAlarmBinding() {
        return BindingBuilder.bind(illegalAlarmQueue()).to(masterExchange()).with("illegalAlarmKey");
    }
    
    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

 @RabbitListener(queues = "illegalAlarmQueue")
    public void receiveMessageIllegalAlarmQueue(Message message, Channel channel) throws IOException {
        IllegalAlarm illegalAlarm = (IllegalAlarm) jsonMessageConverter.fromMessage(message);
        try {
            List<String> paths = new ArrayList<>(6);
            paths.add(illegalAlarm.getTempPicturePath1());
            paths.add(illegalAlarm.getTempPicturePath2());
            paths.add(illegalAlarm.getTempPicturePath3());
            if (channel.isOpen()) {
                List<File> fileEntities = createFileEntities(paths);
                //手动提交事务
                Boolean result = transactionTemplate.execute(status -> {
                    try {
                        IllegalAlarm illegalAlarmTemp = illegalAlarmServiceI.saveIllegalAlarm(illegalAlarm);
                        int res = fileServiceI.saveBatch(fileEntities);
                        if (illegalAlarmTemp != null && res > 0) {
                            log.info("保存完成,id:{}",illegalAlarmTemp.getId());
                            return true;
                        }
                        return false;
                    } catch (Exception e) {
                        log.error("保存记录出现错误,异常信息:{}", e.getMessage());
                        // 回滚事务
                        status.setRollbackOnly();
                        return false;
                    }
                });
                if(result){
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                }
            }
        } catch (Exception e) {
            log.error("{}消费消息错误,异常信息:{}", illegalAlarm.getId(), e.getMessage());
            log.error("详情:{}", JSON.toJSONString(illegalAlarm));
            if (channel.isOpen()) {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            }
        }
    }
spring: 
  rabbitmq:
    host: 192.168.5.100
    port: 5672
    username: guest
    password: guest

以上是我的配置文件。

原因

原因是因为进行了两次消息确认double ack.,配置手动签收模式失效,被注解注入的SimpleRabbitListenerContainerFactory覆盖,而它默认使用了自动签收。

解决

在这里插入图片描述
在这里插入图片描述

配置类

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return factory;
    }
 spring:
  rabbitmq:
    host: 192.168.5.188
    port: 5672
    username: guest
    password: guest
    listener:
      direct:
        acknowledge-mode: manual # 消费端手动ack消息

参考

rabbitmq reply-text=PRECONDITION_FAILED

<think>嗯,用户在使用Celery时遇到了RabbitMQ的amqp.exceptions.PreconditionFailed错误,具体提示是delivery acknowledgement超时。首先,我需要理解这个错误的背景。根据引用[^1],这个错误是由于消息确认在指定时间内未完成导致的,可能和消费者的处理时间过长或配置不当有关。 首先,应该考虑调整消费者的确认超时时间。在Celery中,可以通过配置broker_transport_options来增加这个超时值。比如设置`confirm_publish`和`visibility_timeout`,但需要确认这些参数是否直接关联到消息确认的超时。可能需要查找Celery文档中关于RabbitMQ的配置选项,特别是关于消费者确认的部分。 然后,检查消费者的处理逻辑是否有阻塞或长时间运行的任务。如果任务执行时间超过默认的30分钟(1800000毫秒),可能需要优化任务,分解为更小的子任务,或者增加超时时间。此外,确认是否正确地使用了`acknowledge`,确保在处理完成后及时发送确认信号,避免积压。 接下来,检查RabbitMQ的队列配置。比如队列是否设置了`x-message-ttl`(消息存活时间)或`x-expires`(队列自动删除时间),这些参数如果设置过短,可能导致消息在未被处理前就被删除,从而引发预条件失败的错误。需要确保这些参数合理,或者移除不必要的限制。 另外,查看消费者数量是否足够。如果消费者数量不足,导致消息积压,可能需要增加并发 workers,可以通过Celery的`-c`参数调整。同时,确认网络稳定性,避免因网络问题导致确认信号未能及时送达。 最后,建议用户启用Celery的事件监控,实时观察任务执行状态,使用Flower工具进行可视化管理。如果问题持续,可能需要深入分析堆栈跟踪,或者考虑升级Celery和RabbitMQ的版本,确保兼容性。 生成的相关问题需要覆盖配置调整、错误原因、优化处理、队列设置等方面,帮助用户全面理解并解决类似问题。</think>### Celery RabbitMQ AMQP PreconditionFailed错误解决方案 当Celery使用RabbitMQ出现`amqp.exceptions.PreconditionFailed: delivery acknowledgement on channel 1 timed out`错误时,本质是消息确认机制触发了RabbitMQ的预置条件验证失败。以下是分步解决方案: --- #### 1. 调整消费者确认超时(核心配置) 在Celery配置中增加消息确认超时参数: ```python # Celery配置示例 app.conf.broker_transport_options = { 'confirm_publish': True, # 启用发布确认 'visibility_timeout': 43200000, # 消息可见超时(12小时,单位毫秒) 'ack_timeout': 3600000 # 消费者确认超时(60分钟) } ``` 该配置需与RabbitMQ的`consumer_timeout`参数(默认30分钟)配合使用 --- #### 2. 优化消费者处理逻辑 - **任务拆分**:将耗时超过30分钟的任务拆分为子任务 - **心跳机制**:在长任务中添加定期心跳检测 ```python @app.task(bind=True, soft_time_limit=3600) def long_running_task(self): for i in range(100): do_work() self.update_state(state='PROGRESS', meta={'current': i}) ``` --- #### 3. 检查队列声明参数 通过RabbitMQ管理界面验证队列参数: ```bash # 查看队列参数 rabbitmqctl list_queues name arguments ``` 重点关注: - `x-message-ttl`(消息存活时间) - `x-expires`(队列自动删除时间) - `x-max-length`(队列最大消息数) --- #### 4. 增加消费者并发度 启动worker时指定并发参数: ```bash celery -A proj worker -l info -c 4 --autoscale=10,3 ``` 建议设置: - `-c`:基础并发数(CPU核心数×2) - `--autoscale`:根据负载自动扩展 --- #### 5. 消息确认模式验证 检查任务是否正确配置自动确认: ```python @app.task(acks_late=True) # 推荐使用延迟确认模式 def process_task(data): try: handle_data(data) except Exception: requeue_task(data) ``` --- #### 6. 网络稳定性增强 在RabbitMQ连接配置中添加重试机制: ```python app.conf.broker_connection_retry_on_startup = True app.conf.broker_connection_max_retries = 100 app.conf.broker_connection_retry_delay = 5 # 秒 ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值