正确关闭ActiveMQ和Spring DefaultMessageListenerContainer

正确关闭ActiveMQ和Spring DefaultMessageListenerContainer,activemq,spring-jms,Activemq,Spring Jms,当Tomcat管理器发出“停止”命令时,我们的系统不会关闭。我已经确定它与ActiveMQ/Spring相关。我甚至已经知道了如何关闭它,但我的解决方案是一个黑客(至少我希望这不是一个“正确”的方法)。我想知道关闭ActiveMQ的正确方法,以便删除我的hack 我继承了这个组件,我没有关于为什么做出某些架构决策的信息,经过大量挖掘,我想我理解他的想法,但我可能遗漏了一些东西。换句话说,真正的问题可能在于我们试图使用ActiveMQ/Spring的方式 我们在ServletContainer(T

当Tomcat管理器发出“停止”命令时,我们的系统不会关闭。我已经确定它与ActiveMQ/Spring相关。我甚至已经知道了如何关闭它,但我的解决方案是一个黑客(至少我希望这不是一个“正确”的方法)。我想知道关闭ActiveMQ的正确方法,以便删除我的hack

我继承了这个组件,我没有关于为什么做出某些架构决策的信息,经过大量挖掘,我想我理解他的想法,但我可能遗漏了一些东西。换句话说,真正的问题可能在于我们试图使用ActiveMQ/Spring的方式

我们在ServletContainer(Tomcat 6/7)中运行,并使用ActiveMQ 5.9.1和Spring 3.0.0。我们的应用程序的多个实例可以在一个“组”中运行,每个实例都在自己的服务器上运行。ActiveMQ用于促进多个实例之间的通信。每个实例都有自己的嵌入式代理和自己的队列集。每个实例上的每个队列都有一个org.springframework.jms.listener.DefaultMessageListenerContainer侦听它,因此5个队列=5个DefaultMessageListenerContainers

我们的系统正常关闭,直到我们通过向ConnectionFactory添加queuePrefetch=“0”来修复错误。起初,我认为这种更改在某种程度上是不正确的,但现在我了解了情况,我相信我们不应该使用预取功能

我已经创建了一个测试应用程序来复制这个问题。请注意,下面的信息没有提到消息生产者。这是因为我可以在不发送/处理单个消息的情况下复制问题。只需在引导期间创建代理、ConnectionFactory、队列和侦听器,就足以防止系统正常停止

这是我的SpringXML中的示例配置。如果有人需要,我很乐意提供我的整个项目:

<amq:broker persistent="false" id="mybroker"> 
 <amq:transportConnectors> 
  <amq:transportConnector uri="tcp://0.0.0.0:61616"/> 
 </amq:transportConnectors> 
</amq:broker> 

<amq:connectionFactory id="ConnectionFactory" brokerURL="vm://localhost?broker.persistent=false" > 
 <amq:prefetchPolicy> 
  <amq:prefetchPolicy queuePrefetch="0"/> 
 </amq:prefetchPolicy> 
</amq:connectionFactory> 

<amq:queue id="lookup.mdb.queue.cat" physicalName="DogQueue"/> 
<amq:queue id="lookup.mdb.queue.dog" physicalName="CatQueue"/> 
<amq:queue id="lookup.mdb.queue.fish" physicalName="FishQueue"/> 

<bean id="messageListener" class="org.springframework.jms.listener.DefaultMessageListenerContainer" abstract="true"> 
 <property name="connectionFactory" ref="ConnectionFactory"/> 
</bean> 

<bean parent="messageListener" id="cat"> 
 <property name="destination" ref="lookup.mdb.queue.dog"/> 
 <property name="messageListener"> 
  <bean class="com.acteksoft.common.remote.jms.WorkerMessageListener"/> 
 </property> 
 <property name="concurrentConsumers" value="200"/> 
 <property name="maxConcurrentConsumers" value="200"/> 
</bean> 

<bean parent="messageListener" id="dog"> 
 <property name="destination" ref="lookup.mdb.queue.cat"/> 
 <property name="messageListener"> 
  <bean class="com.acteksoft.common.remote.jms.WorkerMessageListener"/> 
 </property> 
 <property name="concurrentConsumers" value="200"/> 
 <property name="maxConcurrentConsumers" value="200"/> 
</bean> 

<bean parent="messageListener" id="fish"> 
 <property name="destination" ref="lookup.mdb.queue.fish"/> 
 <property name="messageListener"> 
  <bean class="com.acteksoft.common.remote.jms.WorkerMessageListener"/> 
 </property> 
 <property name="concurrentConsumers" value="200"/> 
 <property name="maxConcurrentConsumers" value="200"/> 
</bean> 

我的方法是使用ServletContextListener手动停止对象。令人讨厌的是,我必须创建额外的线程来停止DefaultMessageListenerContainers。也许我按错误的顺序停止了对象,但我已经尝试了我能想象到的一切。如果我试图停止主线程中的对象,那么它们将无限期地挂起

提前谢谢你

更新 根据boday的建议,我尝试了以下方法,但没有成功。我还尝试将amq:transportConnector uri指定为tcp://0.0.0.0:61616?transport.daemon=true

  <amq:broker persistent="false" id="mybroker" brokerName="localhost">
   <amq:transportConnectors>
    <amq:transportConnector uri="tcp://0.0.0.0:61616?daemon=true"/>
   </amq:transportConnectors>
  </amq:broker>

  <amq:connectionFactory id="connectionFactory" brokerURL="vm://localhost" >
   <amq:prefetchPolicy>
    <amq:prefetchPolicy queuePrefetch="0"/>
   </amq:prefetchPolicy>
  </amq:connectionFactory>


有一次,我尝试在amq:connectionFactory元素中向brokerUrl参数添加类似的属性,关机工作正常,但是在进一步测试之后,我了解到这些属性导致从VMTransportFactory抛出异常。这导致初始化不正确,基本消息功能无法工作。

请尝试在TCP传输上设置
daemon=true
,这将允许进程作为deamon线程运行,不会阻止容器关闭


请参阅

,以防其他人感到奇怪,据我所知,使用ActiveMQ不可能有守护进程ListenerContainer

当ActiveMQConnection启动时,它将使用非守护进程线程创建ThreadPoolExecutor。这似乎是为了避免在将连接从一个代理转移到另一个代理时出现问题

executor=new ThreadPoolExecutor(1,1,5,TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadFactory()){
@凌驾
公共线程newThread(可运行的r){
线程线程=新线程(r,“ActiveMQ连接执行器:”+传输);
//不要创建这些守护进程线程-请参阅https://issues.apache.org/jira/browse/AMQ-796
//setDaemon(true);
返回线程;
}
});

boday感谢您的输入,请参阅主要问题描述中的我的更新。
executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, "ActiveMQ Connection Executor: " + transport);
        //Don't make these daemon threads - see https://issues.apache.org/jira/browse/AMQ-796
        //thread.setDaemon(true);
        return thread;
    }
});