【问题标题】:DefaultMessageListenerContainer problems with shutdown procedureDefaultMessageListenerContainer 与关闭过程有关的问题
【发布时间】:2013-01-08 03:20:29
【问题描述】:

我是 Spring Framework 的新手,我的问题如下:

我想以编程方式实例化DefaultMessageListenerContainer,我使用的代码是:

DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(cf);
container.setDestination(Queue);
container.setMessageListener(Consumer);
container.setReceiveTimeout(-1);
container.setMaxConcurrentConsumers(15);
container.setConcurrentConsumers(10);
container.start();

为什么在取消部署我的项目时必须手动关闭DefaultMessageListenerContainer?如果我不手动关闭容器,消费者在我的队列中保持打开状态。

当我尝试手动关闭容器(通过调用container.shutdown())时,程序卡住并且项目无法继续。 如果我初始化DefaultMessageListenerContainer 而不给出receiveTimeout,则正确执行关机程序。 setReceiveTimeout(-1)有问题吗?

【问题讨论】:

    标签: spring jms


    【解决方案1】:

    您只需手动关闭侦听器,因为您已经以编程方式启动了它!如果您使用 ApplicationContext 从 xml 加载 Spring bean,则关闭 App Context 将为您关闭所有 bean。

    我发现控制 Spring 加载的 bean 的最简单方法是创建一个 servlet,它从 HttpServlet 实现 init() 和 destroy() 方法。 Init() 从我的 xml 文件(即名为 spring.xml 的主文件)加载我的 Spring 配置,并缓存 ApplicationContext 对象。然后 destory() 将在 ApplicationContext 上调用 close()。这将关闭/关闭所有 Spring bean(即您的 JMS 侦听器将停止)。

    您以编程方式创建侦听器的任何特定原因?

    【讨论】:

    • 感谢您的回答!我想以编程方式创建我的侦听器,因为我想在运行时更改一些属性。这些属性是:并发消费者,每个队列的最大并发消费者,接收超时。通过这种方式,我可以通过 JMX 提供我的属性并使用新值重新启动侦听器。实例化我的监听器的类实现了 SmartLifecycle。因此侦听器在启动时被初始化并在项目取消部署时被销毁。问题是当我在 setReceiveTimeout 方法中设置 -1 时,当监听器尝试关闭时项目卡住了
    • 查看此类的 Spring 文档,我认为您需要调用 stop() 来停止侦听器,因为 doShutdown() 似乎只是取消注册 JMS 消费者,这可能会像您的消费者一样挂起不断接收(由于超时 = -1)。
    • 我试图在 doShutdown() 之前停止侦听器,但问题仍然存在。执行doShutDown()方法时项目卡住了。
    【解决方案2】:

    您需要的是能够在运行时停止容器(而不是关闭或取消注册)并能够在需要时重新启动它。只需使用 .start() 和 .stop(),我认为它们是从 AbstractJmsListeningContainer 继承的方法。并且不要将它们与 .doStart()、.shutDown() 混合使用...请参阅 spring 文档。

    通过 Spring 连接您的侦听器,您可以随时从上下文中获取它并在其上运行 .stop 或 .start 。在 Spring 自动装配过程中,可以将属性autoStartup 设置为 false,listenerContainer 将被初始化,但在启动时不会做任何监听。

    【讨论】:

      【解决方案3】:

      receiveTimeout 是问题所在。要关闭,容器必须有机会停止侦听队列。如果您的消费者线程有无限超时,它将继续侦听并且永远不会检查容器是否需要关闭。您的容器最多需要receiveTimeout 才能关闭。如果为-1,则永远不会关闭。

      【讨论】:

        【解决方案4】:

        这就是我动态创建新的Listener 并让 Spring 处理 shutdown 过程

        <beans>
        //other beans
        <bean id="importReadQueueDestination" class="org.springframework.jndi.JndiObjectFactoryBean"> 
                <property name="jndiName"><value>queue/dummyQueue</value></property> 
                <property name="resourceRef"><value>true</value></property>
            </bean>
        
            <bean id="importQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> 
                <property name="jndiName"><value>ConnectionFactory</value></property> 
                <property name="resourceRef"><value>true</value></property> 
            </bean>
        
             <bean id="importReadQueueSenderService" class="com.localhost.ImportReadQueueSenderService" scope="prototype"/>
        
            <!-- this is the Message Driven POJO (MDP) -->
            <bean id="importReadMessageListener" class="com.localhost.listener.ImportReadMessageListener" scope="prototype"/>
        
            <!-- and this is the message listener container -->
            <bean id="importReadJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
                <property name="connectionFactory" ref="importQueueConnectionFactory" />
                <property name="destination" ref="importReadQueueDestination" />
                <property name="messageListener" ref="importReadMessageListener" />
                <property name="concurrentConsumers" value="1"/>
        </bean>
        
        </beans>
        

        在这里,我创建了一个虚拟队列 @9​​87654323@,因为 DMLC 需要设置 destinationdestinationName 属性。
        ImportReadMessageListener 扩展 MessageListener。 用于创建和缓存动态侦听器的 Java 代码

        //Actual queue name where I need to send message. `tenantStore` is obtained from ThreadLocalObject
        String queue = tenantStore.getProperty("importReadQueue");
        //Obtaining existing senderService for that queue
        Object queueSenderService = AppConfigurationManager.getQueueSenderService(queue);
        if (queueSenderService != null) {
            ((IImportReadQueueSenderService) queueSenderService).sendObjectMessage(importReadQueueDO);
        } else {
            // In-case of call received from new tenant, then dynamically create and cache a separate listener and DMLC for it
                InitialContext ic = new InitialContext();
                Queue destination = (Queue) ic.lookup(queue);
                ConnectionFactory importQueueConnectionFactory =(ConnectionFactory) ServiceContext.getBean("importQueueConnectionFactory");
        
                JmsTemplate importJmsTemplate=new JmsTemplate(importQueueConnectionFactory);
                importJmsTemplate.setDefaultDestination(destination);
        
                Object importReadMessageListener = ServiceContext.getBean("importReadMessageListener");
        
                DefaultMessageListenerContainer dmlc = (DefaultMessageListenerContainer)ServiceContext.getBean("importReadJmsContainer");
                dmlc.setDestination(destination);
            /*  below two steps are extremely important else you won't receive any message.  
                I already wasted a day behind this.*/
            //  https://stackoverflow.com/a/21364885/4800126
                dmlc.afterPropertiesSet();
                dmlc.start();
        
                IImportReadQueueSenderService importQueueSenderService = (IImportReadQueueSenderService) ServiceContext
                        .getBean("importReadQueueSenderService");
        
                AppConfigurationManager.cacheQueueDetails(queue, dmlc, importQueueSenderService,
                        importReadMessageListener);
                importQueueSenderService.setJmsTemplate(importJmsTemplate);
                importQueueSenderService.sendObjectMessage(importReadQueueDO);
            }  
        

        所以现在当你关闭应用程序时,Spring 会自动关闭所有的监听器。

        【讨论】:

          猜你喜欢
          • 2015-02-25
          • 2011-03-03
          • 2012-10-30
          • 1970-01-01
          • 2022-01-21
          • 2014-07-25
          • 1970-01-01
          • 2022-11-28
          • 1970-01-01
          相关资源
          最近更新 更多