DefaultMessageListenerContainer在关闭过程中的问题

问题描述 投票:4回答:4

我是Spring框架的新手,我的问题如下。

我想实例化 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
4个回答
1
投票

你只需要手动关闭你的监听器,因为你已经以编程的方式启动了它! 如果你使用ApplicationContext从xml加载你的Spring Bean,那么关闭App Context将为你关闭所有的Bean。

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

有什么特别的原因让你在程序上创建你的监听器吗?


1
投票

receiveTimeout 是问题所在。 要关闭,容器必须有机会停止监听队列。 如果你的消费者线程有无限的超时,它将继续监听,永远不会检查容器是否需要关闭。 你的容器将花费多达 receiveTimeout 关机。 如果它是-1,它将永远不会关闭。


0
投票

您在这里需要的是能够仅仅停止容器(而不是关闭或取消注册),并且能够在您想要的时候重新启动它,所有这些都在运行时进行。只要使用.start()和.stop()就可以了,它们是继承于 AbstractJmsListeningContainer 我想。不要把这些和.doStart(), .shutDown()混在一起...... 参见spring文档。

当你的监听器通过Spring布线后,你可以随时从上下文中抓取它,并对它运行.stop或.start。在Spring的自动布线过程中,你可以将属性设置为 autoStartup 为false,并且listenerContainer将被初始化,但不会在启动时进行任何监听。


0
投票

这就是我为了动态创建新的 Listener 且让 春天 处理 关机 程序

<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>

在这里,我已经创建了一个虚拟队列。queue/dummyQueue 因为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会自动关闭所有的监听器。

© www.soinside.com 2019 - 2024. All rights reserved.