2

当系统收到 JMS 消息时,我想刷新我的应用程序上下文。为了做到这一点,我设置了 Spring Integration jms:message-driven-channel-adapter,它将消息转发到实现 ApplicationContextAware 的服务激活器。此激活器(ConfigurationReloader 类)调用 ConfigurableApplicationContext#refresh() 方法。

下面是示例代码片段:

 <jms:message-driven-channel-adapter id="jmsDriverConfigurationAdapter"
    destination="configurationApplyQueue" channel="jmsConfigurationInboundChannel" />

 <channel id="jmsConfigurationInboundChannel"/>

 <service-activator input-channel="jmsConfigurationInboundChannel" ref="configurationReloader" method="refresh"/>

还有我的激活器:

public final class ConfigurationReloader implements ApplicationContextAware {
        private ConfigurableApplicationContext applicationContext;

        public void refresh() {
           this.applicationContext.refresh();
        }

        @Override
        public void setApplicationContext(
                final ApplicationContext applicationContext) throws BeansException {
            if (applicationContext instanceof ConfigurableApplicationContext) {
                this.applicationContext =
                    (ConfigurableApplicationContext) applicationContext;
            }
        }
    }

在传递此类消息的情况下,上下文启动关闭操作但卡在 DefaultMessageListenerContainer bean 关闭:

2011-11-14 15:42:52,980 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Shutting down JMS listener container
2011-11-14 15:42:52,980 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Waiting for shutdown of message listener invokers
2011-11-14 15:42:55,104 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Still waiting for shutdown of 1 message listener invokers

通过 JMS 调用此操作对我来说至关重要,因为新的配置参数与消息一起传递。它是基于最新 SpringCore 和 Spring Integration 的标准 Spring MVC 应用程序,前端带有 DispatcherServlet。另外我确信这是与 JMS 相关的问题,因为通过控制器调用 ConfigurationLoader 可以正常工作。

正如我调试的那样,它在 DefaultMessageListenerContainer#538 行调用(lifecycleMonitor 上的 wait() 方法)之后卡住了:

/**
 * Destroy the registered JMS Sessions and associated MessageConsumers.
 */
protected void doShutdown() throws JMSException {
    logger.debug("Waiting for shutdown of message listener invokers");
    try {
        synchronized (this.lifecycleMonitor) {
            while (this.activeInvokerCount > 0) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Still waiting for shutdown of " + this.activeInvokerCount +
                            " message listener invokers");
                }
                this.lifecycleMonitor.wait();   // <--- line 538
            }
        }
    }
    catch (InterruptedException ex) {
        // Re-interrupt current thread, to allow other threads to react.
        Thread.currentThread().interrupt();
    }
}

...没有人在监视器上调用 notify / notifyAll 所以也许这是某种错误?

感谢您的任何提示!

4

1 回答 1

2

您能解释一下为什么需要如此复杂的架构吗?收到 JMS 消息时重新加载应用程序上下文?听起来很疯狂(或者可能很巧妙?)

不过,我不能 100% 确定,但您提供的信息非常清楚:您正在尝试在使用 JMS 消息时关闭应用程序上下文。但是由于消费者是 Spring 管理的,上下文不能被销毁,因为它等待所有 bean 完成 - 包括ConfigurationReloaderSpring Integration 消息消费者所需的。并且ConfigurationReloader无法完成,因为它等待上下文被销毁(refresh()正在阻塞)。

简单地说——你引入了循环依赖和死锁。

解决方案很简单——延迟上下文刷新,使其在 JMS 消息消费之后发生。最简单的方法是:

public void refresh() {
    Thread destroyThread = new Thread() {
        @Override
        public void run() {
            this.applicationContext.refresh();
        }
    };
    destroyThread.start();
}

不漂亮,但我几乎可以肯定这会奏效。

于 2011-11-14T15:32:15.583 回答