1

I have searched a few on the internet, but have not found anything for me hence posting here.

The error message "#method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out" is well describing the consumer_timeout (ack timeout) happening. I'm using CachingConnectionFactory.

As per the monitor internal, it keeps looping and trying to create a new channel but the channel number is the same as the previous one and again shutting down the same channel. It's not able to restart the consumer. I want to drop that old channel and create a new channel like before. Why its opening the same channel (i.e. channel 8) again after closing? Any suggestion would be a great help.

Below is the sample of the log for ref :

2022-08-09 09:25:27.688  INFO 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-SsW-lSN8lqAg3PUGJr5HDQp identity=2e829d86t] started
2022-08-09 21:27:01.124 ERROR 1 --- [.232.164.84:443] m.m.r.a.client.AmqpConnectionFactory$1   : Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out. Timeout value used: 432000 ms. This timeout value can be configured, see consumers doc guide to learn more, class-id=0, method-id=0)
2022-08-09 21:27:01.124 DEBUG 1 --- [.232.164.84:443] m.m.r.amqp.client.AmqpChannelListener    : AMQP channel closed [false] channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out. Timeout value used: 432000 ms. This timeout value can be configured, see consumers doc guide to learn more, class-id=0, method-id=0)
2022-08-09 21:27:02.641 ERROR 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : Consumer canceled - channel closed SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-SsW-lSN8lqAg3PUGJr5HDQp identity=2e829d86t]
2022-08-09 21:27:02.641 DEBUG 1 --- [enerContainer-1] m.m.r.a.client.AmqpConnectionFactory$1   : Closing cached Channel: PublisherCallbackChannelImpl: AMQChannel(amqp://xxxxxxxxxxxxxxxxx/,8)
2022-08-09 21:27:07.641 DEBUG 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : Attempting to restart consumer SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-SsW-lSN8lqAg3PUGJr5HDQp identity=2e829d86t]

2022-08-09 21:27:07.658 DEBUG 1 --- [enerContainer-1] m.m.r.a.client.AmqpConnectionFactory$1   : Creating cached Rabbit Channel from PublisherCallbackChannelImpl: AMQChannel(amqp://xxxxxxxxxxxx:443/,8)
2022-08-09 21:27:07.658 DEBUG 1 --- [enerContainer-1] m.m.r.amqp.client.AmqpChannelListener    : AMQP channel opened [false] 8
2022-08-09 21:27:07.685  INFO 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-gGFtDxSYhalJ0gWish3voQt identity=3a1d1b7bt] started
2022-08-10 09:27:55.005 ERROR 1 --- [.232.164.84:443] m.m.r.a.client.AmqpConnectionFactory$1   : Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out. Timeout value used: 432000 ms. This timeout value can be configured, see consumers doc guide to learn more, class-id=0, method-id=0)
2022-08-10 09:27:55.005 DEBUG 1 --- [.232.164.84:443] m.m.r.amqp.client.AmqpChannelListener    : AMQP channel closed [false] channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out. Timeout value used: 432000 ms. This timeout value can be configured, see consumers doc guide to learn more, class-id=0, method-id=0)
2022-08-10 09:27:57.641 ERROR 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : Consumer canceled - channel closed SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-gGFtDxSYhalJ0gWish3voQt identity=3a1d1b7bt]
2022-08-10 09:27:57.641 DEBUG 1 --- [enerContainer-1] m.m.r.a.client.AmqpConnectionFactory$1   : Closing cached Channel: PublisherCallbackChannelImpl: AMQChannel(amqp://xxxxxxxxxxxxxxxxxx:443/,8)
2022-08-10 09:27:57.641 DEBUG 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : Attempting to restart consumer SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-gGFtDxSYhalJ0gWish3voQt identity=3a1d1b7bt]
2022-08-10 09:27:57.659 DEBUG 1 --- [enerContainer-1] m.m.r.a.client.AmqpConnectionFactory$1   : Creating cached Rabbit Channel from PublisherCallbackChannelImpl: AMQChannel(amqp://xxxxxxxxxxxxx:443/,8)
2022-08-10 09:27:57.659 DEBUG 1 --- [enerContainer-1] m.m.r.amqp.client.AmqpChannelListener    : AMQP channel opened [false] 8
2022-08-10 09:27:57.687  INFO 1 --- [enerContainer-1] m.m.r.CartMessageListenerContainer    : SimpleConsumer [queue=MYCART.JOBS, consumerTag=amq.ctag-RKgc4e0o1wii-OBE9ai69A identity=68a0033a] started
2022-08-10 21:28:01.145 ERROR 1 --- [.232.164.84:443] m.m.r.a.client.AmqpConnectionFactory$1   : Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - delivery acknowledgement on channel 8 timed out. Timeout value used: 432000 ms. This timeout value can be configured, see consumers doc guide to learn more, class-id=0, method-id=0)

AmqpConnectionFactory.java

@Slf4j
@Configuration
public class AmqpConnectionFactory implements InitializingBean, ApplicationListener<ContextClosedEvent> {
    
    private static final Map<Class<? extends Throwable>,Boolean> EXCEPTION_MAP = new HashMap<>();
    private static final long INITIAL_RETRY_INTERVAL = 1000L;
    private static final int MAX_RETRY_ATTEMPTS = 20;

    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    QueueConfig queueConfig;
    
    private RabbitTemplate rabbitTemplate;
    private RabbitAdmin rabbitAdmin;
    private volatile CachingConnectionFactory cachingConnectionFactory;
    private boolean initialized = false;
    
    @Override
    public void afterPropertiesSet() {
        try {
            initialize();
        } catch (Exception ex) {
            //For now, we're going to say this is ok
        }
    }
    
    public boolean isInitialized() {
        return initialized;
    }
    
    public void reinitialize(String host, String port, boolean useSsl, String username, String password) throws Exception {
        queueConfig.setHost(host);
        queueConfig.setPort(Integer.valueOf(port));
        queueConfig.setSslEnabled(useSsl);
        queueConfig.setUsername(username);
        queueConfig.setPassword(password);
        initialize();
    }

    public void initialize() throws Exception {
        if (!StringUtils.hasLength(queueConfig.getHost()) || !StringUtils.hasLength(queueConfig.getUsername())) {
            log.info("AMQP configuration is disabled or not complete");
            return;
        } 
        log.info("Attempting to connect: {}:{} as {} [ssl={}]", queueConfig.getHost(), queueConfig.getPort(), queueConfig.getUsername(), queueConfig.isSslEnabled());
        if (queueConfig.isSslEnabled()) {
            cachingConnectionFactory = new CachingConnectionFactory(
                new SSLConnectionFactory(queueConfig)) {
                    @Override
                    public void onApplicationEvent(ContextClosedEvent event) {
                        waitForEngineTaskExecutor();
                        super.onApplicationEvent(event);
                    }
                };
        } else {
            cachingConnectionFactory = new CachingConnectionFactory(
                new BaseConnectionFactory(queueConfig)) {
                    @Override
                    public void onApplicationEvent(ContextClosedEvent event) {
                        waitForEngineTaskExecutor();
                        super.onApplicationEvent(event);
                    }
                };
        }
        cachingConnectionFactory.addChannelListener(new AmqpChannelListener());
        cachingConnectionFactory.addConnectionListener(new AmqpConnectionListener());
        cachingConnectionFactory.setChannelCacheSize(queueConfig.getChannelCacheSize());
        cachingConnectionFactory.setConnectionCacheSize(queueConfig.getConnectionCacheSize());
        cachingConnectionFactory.setConnectionLimit(queueConfig.getConnectionLimit());
        cachingConnectionFactory.setConnectionNameStrategy(f -> HostInfo.getHostname());
        cachingConnectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        log.info("Connection Last cachingConnectionFactory {} ", cachingConnectionFactory );
        try {
            rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
            rabbitTemplate.setRetryTemplate(amqpRetryTemplate());
        } catch (Exception ex) {
            log.warn("AMQP credentials may be insufficient for pub/sub capabilities");
        }
        try {
            rabbitAdmin = new RabbitAdmin(cachingConnectionFactory);
            rabbitAdmin.setRetryTemplate(amqpRetryTemplate());
            rabbitAdmin.setIgnoreDeclarationExceptions(true);
        } catch (Exception ex) {
            log.warn("AMQP credentials may be insufficient for Admin capabilities");
        }
        initialized = true;
    }
    
    private void waitForEngineTaskExecutor() {
        try {
            ThreadPoolTaskExecutor engineTaskExecutor = (ThreadPoolTaskExecutor)applicationContext.getBean("engineTaskExecutor");
            if (engineTaskExecutor != null) {
                try {
                    while(engineTaskExecutor.getActiveCount() > 0) {
                        log.warn("There are {} jobs currently executing", engineTaskExecutor.getActiveCount());
                        Thread.currentThread().sleep(10000);
                    }
                    log.info("All jobs have been completed");
                } catch (Exception ex) {
                    log.warn("Termination signal received: {}", ex.getMessage());
                }
            } else {
                log.info("EngineTaskExecutor was not initialized at shutdown");
            }
        } catch (NoSuchBeanDefinitionException ex) {
            //We don't have to wait b/c there is no engineTaskExecutor bean
        }
    }
    
    @Retryable(include = {AmqpAuthenticationException.class, AmqpConnectException.class}, 
               maxAttempts = MAX_RETRY_ATTEMPTS, 
               backoff = @Backoff(delay = INITIAL_RETRY_INTERVAL, 
                                  multiplier = ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, 
                                  maxDelay = ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL))
    public void waitForConnection() throws Exception {
        connect();
    }
    
    @Retryable(include = {AmqpAuthenticationException.class, AmqpConnectException.class}, 
               maxAttempts = Integer.MAX_VALUE, 
               backoff = @Backoff(delay = 5000L, 
                                  multiplier = ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, 
                                  maxDelay = ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL))
    public void waitForConnectionIndefinitely() throws Exception {
        connect();
    }
    
    public boolean isConnected() {
        try {
            connect();
            return true;
        } catch (Exception ex) {
        }
        return false;
    }
    
    private void connect() throws Exception {
        if (!initialized) initialize();
        if (cachingConnectionFactory == null) throw new AmqpConnectException(new RuntimeException("Connection not yet intialized"));
        try {
            cachingConnectionFactory.createConnection();
        } catch (AmqpAuthenticationException | AmqpConnectException ex) {
            log.debug("AMQP connection failure: " + ex.getMessage());
            throw ex;
        }
    }
    
    @Bean
    public RetryTemplate amqpRetryTemplate() {
        //Note this retryTemplate may not handle initial connection failures
        RetryTemplate retryTemplate = new RetryTemplate();
        EXCEPTION_MAP.put(AmqpConnectException.class, true);
        retryTemplate.setRetryPolicy(new SimpleRetryPolicy(MAX_RETRY_ATTEMPTS, EXCEPTION_MAP, true));
        ExponentialBackOffPolicy backOffPolicy = new ExponentialRandomBackOffPolicy();
        backOffPolicy.setInitialInterval(INITIAL_RETRY_INTERVAL);
        backOffPolicy.setMultiplier(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER);
        backOffPolicy.setMaxInterval(ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }

    public RabbitTemplate getRabbitTemplate() {
        try {
            if (!initialized) initialize();
        } catch (Exception ex) {
            log.error("Failed to initialize RabbitTemplate", ex);
        }
        return rabbitTemplate;
    }

    // RabbitAdmin Bean is defined in AmqpRabbitAdmin
    public RabbitAdmin getRabbitAdmin() {
        try {
            if (!initialized) initialize();
        } catch (Exception ex) {
            log.error("Failed to initialize RabbitAdmin", ex);
        }
        return rabbitAdmin;
    }

    public CachingConnectionFactory getConnectionFactory() {
        try {
            if (!initialized) initialize();
        } catch (Exception ex) {
            log.error("Failed to initialize CachingConnectionFactory", ex);
        }
        return cachingConnectionFactory;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent e) {
        waitForEngineTaskExecutor();
    }
}

CartMessageListenerContainer.java

@Slf4j
@Service
public class CartMessageListenerContainer extends DirectMessageListenerContainer implements SmartLifecycle, ApplicationListener<ContextClosedEvent> {
    
    AmqpConnectionFactory amqpConnectionFactory;
    CartMessageListener CartMessageListener;
    LocalWorkerAmqpConfig localWorkerAmqpConfig;
    LocalWorkerConfig localWorkerConfig;
    
    @Autowired
    CartMessageListenerContainer(AmqpConnectionFactory amqpConnectionFactory, CartMessageListener CartMessageListener, LocalWorkerAmqpConfig localWorkerAmqpConfig, LocalWorkerConfig localWorkerConfig) throws Exception {
        
        log.info("Initializing CartMessageListenerContainer...");
        this.amqpConnectionFactory = amqpConnectionFactory;
        this.CartMessageListener = CartMessageListener;
        this.localWorkerAmqpConfig = localWorkerAmqpConfig;
        this.localWorkerConfig = localWorkerConfig;
        
        amqpConnectionFactory.waitForConnection();
        
        this.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        this.setAutoDeclare(Boolean.FALSE);
        this.setAutoStartup(Boolean.TRUE);
        this.setConnectionFactory(amqpConnectionFactory.getConnectionFactory());
        this.setIdleEventInterval(30000);
        this.setMessageListener(CartMessageListener);
        this.setMismatchedQueuesFatal(Boolean.FALSE);
        this.setMissingQueuesFatal(Boolean.FALSE);
        this.setPrefetchCount(localWorkerConfig.getConcurrency());
    }
    
    @Override
    public void start() {
        log.debug("Starting CartMessageListenerContainer...");
        try {
            List<Queue> cartQueues = new ArrayList<>();
            cartQueues.add(localWorkerAmqpConfig.localDefaultQueue());
            if (localWorkerConfig.getDestinationId() != null && !localWorkerConfig.getDestinationId().isEmpty()) {
                try {
                    Long.valueOf(localWorkerConfig.getDestinationId());
                    Queue destinationQueue = new Queue(WorkerQueueNames.getJobsSubscribeToName(localWorkerConfig.getDestinationId()), true, false, false);
                    cartQueues.add(destinationQueue);
                } catch (Exception ex) {
                    log.warn("DestinationId [{}] is invalid or you do not have access to it", localWorkerConfig.getDestinationId());
                }
            }
            this.setQueues(cartQueues.toArray(new Queue[cartQueues.size()]));
            this.lazyLoad();
            super.start();
        } catch (Exception ex) {
            log.error("Failed to start CartMessageListenerContainer", ex);
        }
    }
    
    @Override
    public void stop() {
        log.info("Stopping CartMessageListenerContainer...");
        super.stop();
    }
    
    @Override
    protected void handleListenerException(Throwable ex) {
        //Overriding this b/c default impl logs a stacktrace after the container shuts down
        if (isActive()) {
            // Regular case: failed while active.
            // Invoke ErrorHandler if available.
            invokeErrorHandler(ex);
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent e) {
        log.info("Destroying CartMessageListenerContainer...");
        destroy();
    }
}

BaseConnectionFactory.java

@Slf4j
public class BaseConnectionFactory extends ConnectionFactory {

    protected BaseConnectionFactory(QueueConfig queueConfig) {
        this.setHost(queueConfig.getHost());
        this.setPort(queueConfig.getPort());
        this.setUsername(queueConfig.getUsername());
        this.setPassword(queueConfig.getPassword());
        this.setRequestedHeartbeat(queueConfig.getConnectionTimeout());
        //This ConnectionFactory will be wrapped in Spring CachingConnectionFactory,
        //which prefers to use its own recovery mechanisms
        this.setAutomaticRecoveryEnabled(false);
        
        this.setExceptionHandler(new BaseConnectionExceptionHandler());

        //shutdown consumers once the corresponding queue is deleted rather than notifying them
        Map<String, Object> clientProperties = new HashMap<>();
        clientProperties.put("product", ProductInfo.SHORT_NAME);
        clientProperties.put("information", ProductInfo.LONG_NAME);
        clientProperties.put("copyright", ProductInfo.COPYRIGHT);
        clientProperties.put("version", ProductInfo.VERSION);
        clientProperties.put("hostname", HostInfo.getHostname());
        Map<String, Object> clientCapabilities = new HashMap<>();
        clientCapabilities.put("exchange_exchange_bindings", Boolean.TRUE);
        clientCapabilities.put("consumer_cancel_notify", Boolean.TRUE);
        clientCapabilities.put("basic.nack", Boolean.TRUE);
        clientCapabilities.put("publisher_confirms", Boolean.TRUE);
        clientCapabilities.put("connection.blocked", Boolean.TRUE);
        clientCapabilities.put("authentication_failure_close", Boolean.TRUE);
        clientProperties.put("capabilities", clientCapabilities);
        this.setClientProperties(clientProperties);
    }
    
    protected static class BaseConnectionExceptionHandler extends ForgivingExceptionHandler {
        
        @Override
        public void handleUnexpectedConnectionDriverException(Connection conn, Throwable exception) {
            log.trace("AMQP connection driver failure: {}", exception.getMessage());
        }
        
        @Override
        public void handleConnectionRecoveryException(Connection conn, Throwable exception) {
            // ignore java.net.ConnectException as those are
            // expected during recovery and will only produce noisy
            // traces
            if (exception instanceof ConnectException) {
                // no-op
            } else {
                log.warn("AMQP connection recovery warning: {}", exception.getMessage());
            }
        }
        
        @Override
        public void handleChannelRecoveryException(Channel ch, Throwable exception) {
            log.warn("AMQP channel recovery warning: {}", exception.getMessage());
        }
    }
}
JDGuide
  • 6,239
  • 12
  • 46
  • 64
  • `m.m.r.CartMessageListenerContainer`, `m.m.r.a.client.AmqpConnectionFactory$1` - you are clearly using custom code; I don't see how anyone can help you if you are not using standard framework code. Show your code and configuration, together with version information. – Gary Russell Sep 12 '22 at 13:35
  • Added AmqpConnectionFactory code and CartMessageListenerContainer code. – JDGuide Sep 12 '22 at 14:06
  • According to the logs, it looks like you are using a `BaseConnectionFactory` not a `CachingConnectionFactory`. What is that? – Gary Russell Sep 12 '22 at 14:23
  • Added BaseConnectionFactory. – JDGuide Sep 12 '22 at 14:57
  • It is not reusing the same channel; the channel is closed, but the channel number is reused when the new one is created. For the root cause of the `delivery acknowledgement on channel 8 timed out.` maybe look at the broker logs to see if it provides some clue. Otherwise I suggest you ask about the problem on the `rabbitmq-users` Google group. The RabbitMQ engineers don't monitor stack overflow closely. – Gary Russell Sep 12 '22 at 18:03
  • Thanks, @GaryRussell. I'll check the broker logs and probably try to post on rabbitmq-users group. – JDGuide Sep 12 '22 at 18:32
  • In broker logs its similar error message "delivery acknowledgement on channel". I'll try to connect to rabbitmq-users group. – JDGuide Sep 13 '22 at 12:58
  • @JDGuide did you ever get anywhere with this? I'm facing the same problem in my application and I'm not sure how to find which message is causing this issue. – mjaggard Sep 22 '22 at 11:47
  • 1
    @mjaggard we have ended with handling the ack in a better way, and also we have configured the ack timeout with a higher value. – JDGuide Sep 22 '22 at 13:13

0 Answers0