将Spring-AMQP从1.2.0升级到1.3.0时出错

时间:2014-09-29 17:52:19

标签: java rabbitmq spring-amqp

我们的应用程序已构建并运行于: -
spring.version = 3.2.8.RELEASE
spring.amqp.version = 1.2.0.RELEASE
rabbitmq.version = 3.1.3(客户端)
RabbitMQ Server版本为3.1.5

我们想将rabbitmq服务器从3.1.5升级到3.3.5,我们成功地做到了。

现在我们想升级应用程序以使用最新版本的spring-amqp,RabbitMQ java客户端,因此我们升级了以下组件: -


spring.version = 3.2.8.RELEASE
spring.amqp.version = 1.3.0.RELEASE
rabbitmq.version = 3.2.4(客户端)
RabbitMQ Server版本为3.3.5

然而,在升级到spring-amqp到1.3.0后,我们的应用程序开始挂起。基本上我们在应用程序启动期间启动了许多监听器容器,并且启动每个监听器容器现在需要60秒才能进入下一步

挖掘深度后我发现,程序在SimpleMessageListenerContainer类的run()方法中挂起: -

org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer

    public void run() {

        boolean aborted = false;

        int consecutiveIdles = 0;

        int consecutiveMessages = 0;

        try {

            try {
                SimpleMessageListenerContainer.this.redeclareElementsIfNecessary(); // Here is where the programing thread hangs.
                this.consumer.start();
                this.start.countDown();
            }

如上所述代码,线程在redeclareElementsIfNecessary()方法中挂起,并且此方法仅在此版本的spring-rabbit中引入。我不知道为什么它会挂在那里,无论我传递给这个SimpleMessageListenerContainer的任何建议参数,它似乎都没有工作。

如果我使用新的RabbitMQ服务器3.3.5恢复到spring-amqp 1.2.0版本,似乎一切正常,但是新的spring-amqp客户端无法正常工作。

我现在被困在这里几天了。 Spring / Rabbitmq大师在那里,你能帮我解决这个问题吗?


感谢您的快速回复,但似乎代码没有达到这一点,它只是挂在您提供的代码段上方, 我在下面评论代码挂在哪里

Set<String> queueNames = this.getQueueNamesAsSet();
Map<String, Queue> queueBeans = ((ListableBeanFactory) applicationContext).getBeansOfType(Queue.class); // The code started to hung here
for (Entry<String, Queue> entry : queueBeans.entrySet()) {
    Queue queue = entry.getValue();
    if (queueNames.contains(queue.getName()) && queue.isAutoDelete()
            && this.rabbitAdmin.getQueueProperties(queue.getName()) == null) {
        if (logger.isDebugEnabled()) {
            logger.debug("At least one auto-delete queue is missing: " + queue.getName()
                    + "; redeclaring context exchanges, queues, bindings.");
        }
        this.rabbitAdmin.initialize();
        break;
    }
}

实际上我们只升级到了spring-amqp的最新版本,即

spring.version = 3.2.8.RELEASE 
spring.amqp.version = 1.3.6.RELEASE 
rabbitmq.version = 3.3.4 (client) 
RabbitMQ Server version is 3.3.5
然而,我们遇到了完全相同的问题,所以只是为了找出问题从哪个版本开始我跑到1.3.0的低版本,似乎问题是从spring-amqp本身的1.3.0版本开始。这就是原因。

我附上了所请求的信息,包括仅基于spring-amqp 1.3.6的线程转储。

这是程序挂起的监听器容器的配置,你可以看到我们有自己的SimpleMessageLinstenerContainer,它充当了acutal spring的SimpleMessageListenerContainer的包装器, 我还附上了这个自定义包装文件供您参考。

<bean id="tlogOutOfCycleMessageListenerPrototype" class="com.myorg.ips.cnccommon.support.amqp.SimpleMessageListenerContainer" scope="prototype">
    <property name="channelTransacted" value="true" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="concurrentConsumers" value="1" />
    <property name="taskExecutor" ref="tlogOutOfCycleMessageListenerPool" />
    <property name="messageListener" ref="tlogMLAOutOfCycle" />
    <property name="errorHandler" ref="tlogOutOfCycleMessageHandler" />
    <property name="autoStartup" value="false" />
    <property name="instanceNameForLogging" value="site1TlogOutOfCycleMessageListener"/>
    <!-- A dummy connection factory which will never be used -->
    <property name="connectionFactory" ref="switchCompositeConnectionFactoryPrototype"/>
</bean>

我们的包装类SimpleMessageListenerContainer.java

    package com.myorg.ips.cnccommon.support.amqp;

    import org.apache.commons.lang.builder.ToStringBuilder;
    import org.slf4j.cal10n.LocLogger;
    import org.springframework.util.ErrorHandler;

    import com.myorg.ips.amqp.SwitchSiteSupport;
    import com.myorg.ips.logging.LoggerFactory;
    import com.myorg.ips.system.config.InitialisableSiteAware;

    import static com.myorg.ips.logging.SystemWideLogMessages.ERROR_AMQP_FAILED_TO_START_LISTENER;
    import static com.myorg.ips.logging.SystemWideLogMessages.INFO_AMQP_STOPPING_LISTENER;

    /**
     *
     * Wrapper for the Spring SimpleMessageListenerContainer which simply allows us to delay (or prevent startup). Can also restart on command.
     *
     */
    public class SimpleMessageListenerContainer extends org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer implements InitialisableSiteAware {
        private static final LocLogger logger = LoggerFactory.getLogger(SimpleMessageListenerContainer.class);
        private boolean autoStart = true;
        private ErrorHandler exposedErrorHandler;

        private boolean springBeanInitialisationAttempted = false;

        private boolean springBeanInitialised = false;

        private String instanceNameForLogging;

        @Override
        public void initialize() {
            // Do nothing -- we will instead perform the Spring bean initialisation later on via the factory bean, after the connection factory has been set
            springBeanInitialisationAttempted = true;
            springBeanInitialised = false;
        }

        @Override
        public void initialise() {
            SwitchSiteSupport.initialiseIfSiteAware(getMessageListener());
            SwitchSiteSupport.initialiseIfSiteAware(getErrorHandler());

            // If this object is a Spring bean, we should now complete the initialisation that the Spring framework attempted earlier
            if (springBeanInitialisationAttempted && !springBeanInitialised) {
                springBeanInitialised = true;
                super.initialize();
                if (isAutoStartup()) {
                    start();
                }
            }
        }

        @Override
        public void configureForSite(final MultiHostConnectionFactory configuredConnectionFactory) {
            setConnectionFactory(configuredConnectionFactory);
            SwitchSiteSupport.configureIfSiteAware(getMessageListener(), configuredConnectionFactory);
            SwitchSiteSupport.configureIfSiteAware(getErrorHandler(), configuredConnectionFactory);

            setInstanceNameForLogging(SwitchSiteSupport.replaceWithSiteAlias(instanceNameForLogging, configuredConnectionFactory));
        }

        @Override
        //CHECKSTYLE:OFF Unfortunately the parent springframework class throws and exception, so so do we
        protected void doStart() throws Exception {
            //CHECKSTYLE:ON
            if (autoStart) {
                logger.debug("Starting message listener " + instanceNameForLogging);
                super.doStart();
                logger.debug("Started message listener " + instanceNameForLogging);
            }
        }

        /**
         * Start this listener
         */
        public void start() {
            autoStart = true;
            try {
                doStart();
                //CHECKSTYLE:OFF Unfortunately the parent springframework class throws and exception, so that is what we catch
            } catch (Exception e) {
                //CHECKSTYLE:ON
                logger.error(ERROR_AMQP_FAILED_TO_START_LISTENER, e);
            }
        }

        /**
         * Stop listener
         */
        public void stop() {
            logger.info(INFO_AMQP_STOPPING_LISTENER, getBeanName());
            autoStart = false;
            doStop();
        }

        /**
         * Stop and start this listener
         */
        public void restart() {
            stop();
            start();
        }

        /**
         * Store the errorHandler in a subclass-specific property so that we can retrieve it later
         * @param errorHandler errorHandler
         */
        @Override
        public void setErrorHandler(final ErrorHandler errorHandler) {
            this.exposedErrorHandler = errorHandler;
            super.setErrorHandler(errorHandler);
        }

        /**
         * Return the exposed errorHandler
         * @return errorHandler
         */
        public ErrorHandler getErrorHandler() {
            return exposedErrorHandler;
        }

        public void setInstanceNameForLogging(final String instanceNameForLogging) {
            this.instanceNameForLogging = instanceNameForLogging;
        }

        @Override
        public String toString(){
            return ToStringBuilder.reflectionToString(this);
        }
    }

1 个答案:

答案 0 :(得分:0)

反正好抓!

我现在正在使用Spring AMQP 1.4的代码。

你介意分享:

  1. 您挂起的ListenerContainer的配置
  2. 来自redeclareElementsIfNecessary()
  3. 的代码的调试分析

    实际上现在代码看起来像是:

    if (queueNames.contains(queue.getName()) && queue.isAutoDelete()
            && this.rabbitAdmin.getQueueProperties(queue.getName()) == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("At least one auto-delete queue is missing: " + queue.getName()
                                    + "; redeclaring context exchanges, queues, bindings.");
                }
                this.rabbitAdmin.initialize();
                break;
    }
    

    因此,它可能只会在auto-delete队列中解除。

    或者你有另一张照片吗?..

    <强>更新

    根据你的ThreadDump。这是非法的:

    at com.vocalink.ips.amqp.AmqpMessageListenerManager.initialise(AmqpMessageListenerManager.java:106)
            at com.vocalink.ips.amqp.SwitchSiteSupport.initialiseIfSiteAware(SwitchSiteSupport.java:29)
            at com.vocalink.ips.system.config.AbstractSiteAwareComponentCachingFactory.createAndConfigureSiteAwareComponent(AbstractSiteAwareComponentCachingFactory.java:51)
    

    您无法在初始化阶段start组件。或者将它留给容器,或者只是在运行时在某个地方手动执行start,此时已经创建了所有bean。

    例如,您可以使用ApplicationListener<ContextRefreshedEvent>

    执行此操作