2011-06-21 111 views
50

在我的JMS应用程序上,我们使用生产者上的临时队列来接收消费者应用程序的回复。ActiveMQ:如何在使用临时队列时处理代理故障转移

我面对我的结束正是同样的问题在这个线程中提到:http://activemq.2283324.n4.nabble.com/jira-Created-AMQ-3336-Temporary-Destination-errors-on-H-A-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 

为AU rl param在客户端brokerURL上。我立即用这个附加参数更改了我的客户端代理网址。

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. 我已经在JMSCorrelationID标头中发送了每个消息的唯一关联标识符。
  3. 据我所知即使骆驼和Spring也使用临时队列来请求响应机制。唯一的区别是Spring JMS实现为每条消息创建并销毁临时队列,而我为生产者的生命周期创建临时队列。当客户端(生产者)应用程序关闭时,此临时队列会被销毁,或者AMQ代理在意识到没有活动的生产者与此临时队列连接时会被销毁。
  4. 我已经在Producer端的每条消息上设置了消息到期时间,以便消息不会在队列中滞留太久(60秒)。
+1

新的'JMSException'是刚登录还是抛入客户端代码?另外,客户端发送给代理的每条消息是否抛出异常,或者故障转移完成时异常是否停止? (即只有在客户端没有连接的时候抛出异常?) – Bringer128

+2

[似乎是](http://activemq.apache.org/advisory-message.html#AdvisoryMessage-Disablingadvisorymessages)有几件事情您需要在XML配置中添加'jms.watchTopicAdvisories = false',即'',并静态配置您的网络。 (你的amq1.xml文件给我404 Not Found) – opyate

+1

@ Bringer128:感谢您的评论。该JMS异常在重新连接后生产者连接的其他AMQ代理上引发。而且一旦发生这种情况,JMS生产者只是停止接收来自消费者的任何响应,因为AMQ代理不能将回复发回给具有上述JMS异常的生产者。 – anubhava

回答

24

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

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

但有一点需要注意,我在activemq-core上看不到任何使用该属性的测试,所以我不能保证这个。

+1

如果您发现我在我的问题中写道:'每当我重新启动我的网络中的任意经纪人'。所以我想知道如果代理本身正在重新启动,这个代理属性'cacheTempDestinations'会有什么效果吗? – anubhava

+4

当您重新启动代理时,任何临时目标将连同任何挂起的消息都将丢失。经纪人没有为临时目的地维护持续状态。 – gtully

+0

我们能做的最好的是:1)支持故障转移:重新连接到现有的代理。 2)如果在代理之间进行网络分区,或者在发送对临时目标的回复之前代理发生故障,则允许自动创建临时目标。 – gtully

9

在请求回复场景中的请求者(生产者)所连接的代理上创建临时队列。它们是从javax.jms.Session创建的,所以在该会话断开连接时,无论是因为客户端断开还是代理失败/故障转移,这些队列都会永久消失。其他经纪人都不会理解当你的一个消费者试图回复这些队列时的含义;因此你的例外。

这需要思维模式的架构转变,假设您想要处理故障转移并保留所有消息。以下是您可以攻击该问题的一般方法:

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

这是一个类似于Apache Camelrequest-response over messaging的方法。

需要注意的一点是,当客户端执行时,队列不会消失,因此您应该设置一个时间来响应消息,以便从代理中删除(如果它没有被使用)否则你会收到未消费的消息。您还需要设置一个dead letter queue strategy to automatically discard expired messages

+0

感谢你的详细答案。我在我的问题中添加了更新部分,以回应您的所有观点。你是否有机会建议我不要使用每个生产者的临时队列**? – anubhava

+0

这是正确的,临时队列不是为故障转移而设计的 - 它们是客户端和代理之间通过会话的合同;该会话位于单独的连接上。 Camel在默认情况下通过临时队列进行请求回复,但这对于所列出的原因通常是不切实际的,并且因此支持静态队列作为回退来满足服务质量(尽管它使用队列中的JMS选择器进行关联,由于性能原因而被避免)。 –

+0

IMO假定'临时队列不是为故障转移而设计的'是不正确的。同样,如果没有使用临时队列,那么如果你事先不知道所有的生产者,你怎么能拥有“一个特定于请求者进程的队列”,或者换句话说,每个生产者。 – anubhava

相关问题