54

在我的 JMS 应用程序中,我们使用生产者上的临时队列来接收来自消费者应用程序的回复。

我面临着与此线程中提到的完全相同的问题:http: //activemq.2283324.n4.nabble.com/jira-Created-AMQ-3336-Temporary-Destination-errors-on-HA-failover-in -broker-network-with-Failover-tt-td3551034.html#a3612738

每当我重新启动网络中的任意代理时,在尝试向临时队列发送回复时,我的消费者应用程序日志中都会出现许多类似的错误:

javax.jms.InvalidDestinationException:
  Cannot publish to a deleted Destination: temp-queue://ID:...

然后我看到加里的回应建议使用

jms.watchTopicAdvisories=false

作为客户端上的 url 参数brokerURL。我立即使用这个附加参数更改了我的客户代理 URL。但是,现在当我在网络中重新启动代理以进行故障转移测试时,我看到了这样的错误:

javax.jms.JMSException: 
  The destination temp-queue:
    //ID:client.host-65070-1308610734958-2:1:1 does not exist.

我正在使用 ActiveMQ 5.5 版本。我的客户代理 URL 如下所示:

failover:(tcp://amq-host1:61616,tcp://amq-host2.tred.aol.com:61616,tcp://amq-host3:61616,tcp://amq-host4:61616)?jms.useAsyncSend=true&timeout=5000&jms.watchTopicAdvisories=false
 

此外,这是我为 4 个代理之一的 activemq 配置 XML: amq1.xml

有人可以在这里调查这个问题并建议我在这个设置中犯了什么错误。

更新:

为了进一步阐明我如何在我的代码中进行请求响应:

  1. 我已经使用了每个生产者目的地(即临时队列)并将其设置在每条消息的回复标头中。
  2. 我已经在 J​​MSCorrelationID 标头中发送了每条消息的唯一相关标识符。
  3. 据我所知,甚至 Camel 和 Spring 也在使用临时队列来实现请求-响应机制。唯一的区别是 Spring JMS 实现为每条消息创建和销毁临时队列,而我为生产者的生命周期创建临时队列。当客户端(生产者)应用程序关闭时,该临时队列将被销毁,或者当 AMQ 代理意识到此临时队列没有附加活动生产者时,它会被销毁。
  4. 我已经在生产者端的每条消息上设置了一个消息过期,这样消息就不会在队列中停留太久(60 秒)。
4

2 回答 2

27

有一个代理属性 org.apache.activemq.broker.BrokerService#cacheTempDestinations 应该有助于故障转移:案例。在 xml 配置中将其设置为 true,当客户端断开连接时,不会立即删除临时目标。快速故障转移:重新连接将能够再次从临时队列中生产和/或消费。

有一个基于 timeBeforePurgeTempDestinations(默认 5 秒)的计时器任务来处理缓存删除。

不过需要注意的是,我在 activemq-core 中没有看到任何使用该属性的测试,因此我无法对此提供任何保证。

于 2012-01-26T16:02:03.000 回答
10

临时队列是在请求-回复场景中的请求者(生产者)所连接的代理上创建的。它们是从 a 创建的javax.jms.Session,因此在该会话断开连接时,无论是由于客户端断开连接还是代理失败/故障转移,这些队列都将永久消失。当您的一个消费者尝试回复这些队列时,其他代理都不会理解这意味着什么;因此你的例外。

假设您要处理故障转移并保留所有消息,这需要在思维方式上进行架构转变。这是您可以解决问题的一般方法:

  1. 您的回复标头应引用特定于请求者进程的队列:例如queue:response.<client id>. 如果您的客户端数量有限,则客户端 ID 可能是标准名称;如果您拥有大量客户端,则可能是 UUID。
  2. 出站消息应该设置一个相关标识符(只是一个让您将请求与响应相关联的字符串 - 毕竟请求者可能同时发出多个请求)。这是在JMSCorrelationID标头中设置的,应该从请求复制到响应消息。
  3. 请求者需要在该队列上设置一个侦听器,该侦听器将根据该关联 ID 将消息正文返回给请求线程。为此需要编写一些多线程代码,因为您需要手动管理一些东西,例如将相关 ID 映射到原始线程(可能通过 Futures)。

这与Apache Camel针对消息传递的请求响应所采用的方法类似。

需要注意的一件事是,当客户端消失时,队列不会消失,因此您应该设置一个时间来处理响应消息,这样如果它没有被消费,它就会从代理中删除,否则你会得到未使用的消息积压。您还需要设置死信队列策略来自动丢弃过期消息

于 2012-01-25T13:38:52.863 回答