【问题标题】:Properly Shutting Down ActiveMQ and Spring DefaultMessageListenerContainer正确关闭 ActiveMQ 和 Spring DefaultMessageListenerContainer
【发布时间】:2014-07-25 19:31:04
【问题描述】:

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

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

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

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

我创建了一个测试应用程序来复制该问题。请注意,以下信息未提及消息生产者。那是因为我可以在不发送/处理单个消息的情况下复制问题。只需在启动期间创建 Broker、ConnectionFactory、Queues 和 Listeners,就足以防止系统正常停止。

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

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

我的 hack 涉及使用 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 抛出异常。这导致初始化不正确,并且基本的消息功能不起作用。

【问题讨论】:

    标签: activemq spring-jms


    【解决方案1】:

    如果其他人想知道,据我所知,不可能使用 ActiveMQ 拥有守护进程 ListenerContainer。

    当 ActiveMQConnection 启动时,它会创建一个带有非守护线程的 ThreadPoolExecutor。这似乎是为了避免从一个代理到另一个代理的连接故障转移时出现问题。

    https://issues.apache.org/jira/browse/AMQ-796

    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;
        }
    });
    

    【讨论】:

      【解决方案2】:

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

      http://activemq.apache.org/tcp-transport-reference.html

      【讨论】:

      • boday 感谢您的输入,请在主要问题描述中查看我的更新。
      猜你喜欢
      • 2012-08-16
      • 2011-08-13
      • 2011-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-30
      相关资源
      最近更新 更多