使用经纪人网络中的临时队列进行请求/回复模式的ActiveMQ / Camel故障转移 - 无法发布到已删除的临时队列

时间:2016-03-15 08:28:21

标签: java apache-camel activemq failover

由于扩展原因,我们最近从单个ActiveMQ代理切换到代理网络。虽然在大多数情况下一切都与预期完全一致,但在白天经纪人重新部署后我们遇到了一个奇怪的问题:

首先,对于技术堆栈,我们使用ActiveMQ 5.12.1和Camel 2.13.4来实现java方法和JMS端点之间的集成。经纪人方是一个经纪人网络,目前由3名成员组成,使用以下配置

<broker useJmx="${activemq.expose.jmx}" persistent="false"
    brokerName="${activemq.brokerName}" xmlns="http://activemq.apache.org/schema/core">
    <sslContext>
        <amq:sslContext keyStore="${activemq.broker.keyStore}"
            keyStorePassword="${activemq.broker.keyStorePassword}"
            trustStore="${activemq.broker.trustStore}"
            trustStorePassword="${activemq.broker.trustStorePassword}" />
    </sslContext>
    <systemUsage>
        <systemUsage>
            <memoryUsage>
                <memoryUsage limit="${activemq.memoryUsage}" />
            </memoryUsage>
            <tempUsage>
                <tempUsage limit="${activemq.tempUsage}" />
            </tempUsage>
        </systemUsage>
    </systemUsage>
    <destinationPolicy>
        <policyMap>
            <policyEntries>
                <policyEntry queue=">" enableAudit="false">
                    <networkBridgeFilterFactory>
                        <conditionalNetworkBridgeFilterFactory
                            replayWhenNoConsumers="true" />
                    </networkBridgeFilterFactory>
                </policyEntry>
            </policyEntries>
        </policyMap>
    </destinationPolicy>
    <networkConnectors>
        <networkConnector name="queues"
            uri="static:(${activemq.otherBrokers})"
            networkTTL="2" dynamicOnly="true"
            decreaseNetworkConsumerPriority="true"
            conduitSubscriptions="false">
            <excludedDestinations>
                <topic physicalName=">" />
            </excludedDestinations>
        </networkConnector>
        <networkConnector name="topics"
            uri="static:(${activemq.otherBrokers})"
            networkTTL="1" dynamicOnly="true"
            decreaseNetworkConsumerPriority="true"
            conduitSubscriptions="true">
            <excludedDestinations>
                <queue physicalName=">" />
            </excludedDestinations>
        </networkConnector>
    </networkConnectors>
    <transportConnectors>
        <transportConnector
            uri="${activemq.protocol}${activemq.host}:${activemq.tcp.port}?needClientAuth=true"
            updateClusterClients="true" rebalanceClusterClients="true" />
        <transportConnector
            uri="${activemq.websocket.protocol}${activemq.websocket.host}:${activemq.websocket.port}?needClientAuth=true"
            updateClusterClients="true" rebalanceClusterClients="true" />
    </transportConnectors>
</broker>

使用以下占位符值

activemq.tcp.port=9000
activemq.protocol=ssl://
activemq.brokerName=activemq-server1.com
activemq.expose.jmx=true
activemq.otherBrokers=ssl://server2.com:9000,ssl://server3.com:9000
activemq.websocket.port=9001
activemq.websocket.protocol=stomp+ssl://
activemq.websocket.host=server1.com
activemq.memoryUsage=1gb
activemq.tempUsage=1gb

在客户端,正在使用以下camel配置

<bean id="xxx.activemq.redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
    <property name="maximumRedeliveries" value="0" />
</bean>

<bean id="xxx.activemq.jmsConnectionFactory" class="org.apache.activemq.ActiveMQSslConnectionFactory">
    <property name="trustStore" value="${activemq.broker.trustStore}" />
    <property name="trustStorePassword" value="${activemq.broker.trustStorePassword}" />
    <property name="keyStore" value="${activemq.broker.keyStore}" />
    <property name="keyStorePassword" value="${activemq.broker.keyStorePassword}" />
    <property name="brokerURL" value="${activemq.broker.url}" />
    <property name="redeliveryPolicy" ref="xxx.activemq.redeliveryPolicy" />
</bean>

<bean id="xxx.activemq.jmsConfiguration" class="org.apache.activemq.camel.component.ActiveMQConfiguration">
    <property name="receiveTimeout" value="6000" />
    <property name="connectionFactory" ref="xxx.activemq.pooledConnectionFactory" />
</bean>

<bean id="xxx.activemq.pooledConnectionFactory"
    class="org.apache.activemq.pool.PooledConnectionFactory"
    init-method="start" destroy-method="stop">
    <property name="maxConnections" value="8" />
    <property name="idleTimeout" value="0" />
    <property name="timeBetweenExpirationCheckMillis"
        value="10000" />
    <property name="connectionFactory"
        ref="xxx.activemq.jmsConnectionFactory" />
</bean>

<bean id="xxx.activemq.jms.abstractComponent" abstract="true"
    class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="configuration"
        ref="xxx.activemq.jmsConfiguration" />
    <property name="connectionFactory"
        ref="xxx.activemq.pooledConnectionFactory" />
    <property name="allowNullBody" value="true" />
    <property name="transferException" value="true" />
    <property name="defaultTaskExecutorType"
        value="#{T(org.apache.camel.component.jms.DefaultTaskExecutorType).ThreadPool}" />
    <property name="requestTimeout" value="5000" />
</bean>

<bean id="xxx.activemq.jms.queue"
    parent="xxx.activemq.jms.abstractComponent">
    <property name="concurrentConsumers" value="2" />
    <property name="maxConcurrentConsumers" value="2" />
</bean>

连接网址为

activemq.broker.url=failover:(ssl://server1.com:9000,ssl://server2.com:9000,ssl://server3.com:9000)?randomize=true

请求/回复EIP是通过让生产者设置一个相应的jmsReplyTo头并使用temp-queues将camel默认设置为InOut来实现的。

在部署之前,所有消息都按预期工作,但是之后对于某些请求/回复队列,我们​​将开始在生产者端获得超时。以下条目显示在日志中:

在生产者方面

Caused by: org.apache.camel.ExchangeTimedOutException: The OUT message was not received within: 5000 millis due reply message with correlationID: Camel-ID-xxx-intranet-phs-49404-1457684675710-8-11 not received on destination: temp-queue://ID:xxx.intranet.phs-41986-1457684806758-1:3:1. 
Exchange[Message: BeanInvocation public abstract xxx.xxx.rapi.dto.RemoteDTO xxx.xxx.xxx.facade.RemoteFacade.findRemoteDTO(java.lang.String,java.lang.Long) with [xxx, 31333]]] 

在消费者方面:

Caused by: javax.jms.InvalidDestinationException: Cannot publish to a deleted Destination: temp-queue://ID:xxx.intranet.phs-41986-1457684806758-1:3:1 

我们从那以后做了一些研究,发现每当网络的任意代理关闭时,问题都会出现,然后只有那些在关机命中时有临时队列打开回复的生产者才会出现问题故障转移到新的代理。之后问题会持续到这个制作人,直到他重新启动。一旦他重新启动后重新加入,一切都恢复正常。 grokbase以及activemq-failover-with-temporary-queues-on-a-network-of-brokersactivemq-how-to-handle-broker-failovers-while-using-temporary-queues两个主题也描述了该问题。我们已经尝试了activemq-how-to-handle-broker-failovers-while-using-temporary-queues中给出的一个解决方案来设置缓存超时但没有从中得到任何结果,另一个建议的选择来转变客户端的咨询监听并不是我们设置中的一个选项,因为我们想要利用clusterRebalancing等功能,可以在运行时更轻松地向网络添加其他代理。

我们还发现了一些关于驼峰和ActiveMQ方面的JIRA问题,如CAMEL-3193描述了这个问题,并且显然已将它们修复为我们更新的版本,所以我们非常困惑。目前我们正在考虑通过临时队列切换到独占回复队列来解决这个问题,但首先想问一下我们是否只是遗漏了某些配置。

如果您需要任何其他信息,请询问!

1 个答案:

答案 0 :(得分:0)

请参阅AMQ-5469:当建立连接的连接断开时,将删除临时队列。关闭代理时会发生这种情况。

解决方案可以是以下两个选项之一:

  • 客户端在重新建立代理连接
  • 时重新创建临时队列
  • 不要使用临时队列,使用持久队列