spring-amqp

spring-amqp: Channel shutdown with NACKS RECEIVED message


Using spring-amqp with heavy load on RabbitMQ bus, we sometimes get logs from org.springframework.amqp.rabbit.connection.CachingConnectionFactory saying : Channel shutdown: clean channel shutdown; protocol method: #method<channel.close>(reply-code=200, reply-text=NACKS RECEIVED, class-id=0, method-id=0)

Can you explain this log, please, and why is it at ERROR level? Do we have any adjustments to make? Thanks in advance for your answer.


Solution

  • The channel throws an exception if all publisher confirms are not returned with the timeout...

    @Override
    public void waitForConfirmsOrDie(long timeout)
        throws IOException, InterruptedException, TimeoutException
    {
        try {
            if (!waitForConfirms(timeout)) {
                close(AMQP.REPLY_SUCCESS, "NACKS RECEIVED", true, null, false);
                throw new IOException("nacks received");
            }
        } catch (TimeoutException e) {
            close(AMQP.PRECONDITION_FAILED, "TIMEOUT WAITING FOR ACK");
            throw(e);
        }
    }
    

    The DefaultChannelCloseLogger will only skip normal closes (200) if the reply text is OK...

    /**
     * Return true if the {@link ShutdownSignalException} reason is AMQP.Channel.Close and
     * the reply code was AMQP.REPLY_SUCCESS (200) and the text equals "OK".
     * @param sig the exception.
     * @return true for a normal channel close.
     */
    public static boolean isNormalChannelClose(ShutdownSignalException sig) {
        Method shutdownReason = sig.getReason();
        return isNormalShutdown(sig) ||
                (shutdownReason instanceof AMQP.Channel.Close
                    && AMQP.REPLY_SUCCESS == ((AMQP.Channel.Close) shutdownReason).getReplyCode()
                    && "OK".equals(((AMQP.Channel.Close) shutdownReason).getReplyText()));
    }
    

    If you want to ignore these errors, you can configure a custom close exception logger:

    /**
     * Set the strategy for logging close exceptions; by default, if a channel is closed due to a failed
     * passive queue declaration, it is logged at debug level. Normal channel closes (200 OK) are not
     * logged. All others are logged at ERROR level (unless access is refused due to an exclusive consumer
     * condition, in which case, it is logged at INFO level).
     * @param closeExceptionLogger the {@link ConditionalExceptionLogger}.
     * @since 1.5
     */
    public void setCloseExceptionLogger(ConditionalExceptionLogger closeExceptionLogger) {
        Assert.notNull(closeExceptionLogger, "'closeExceptionLogger' cannot be null");
        this.closeExceptionLogger = closeExceptionLogger;
        if (this.publisherConnectionFactory != null) {
            this.publisherConnectionFactory.setCloseExceptionLogger(closeExceptionLogger);
        }
    }