如何在ActiveMQ中使用Stomp ACK“client-individual”?

时间:2018-01-25 09:38:44

标签: java apache-camel activemq stomp

我想用ActiveMQ代理和非常基本的Stomp客户端实现一个设置,它一次只能处理一条消息(请求 - 回复)。

我在Spring(5.0.0.RELEASE)Boot(1.5.7.RELEASE)+ Apache Camel(2.20.0)JMS应用程序中使用嵌入式ActiveMQ(5.15.1)代理,以及用于Stomp客户端Spring Boot使用Apache Camel的Stomp实现。 “main”应用程序接收HTTP POST,确定相应的负责队列,并使用replyTo连接器通过JMS(Apache Camel ActiveMQComponent)将POST主体作为InOut(vm://localhost选项)消息发送到嵌入式代理。 Stomp客户端通过TCP连接到代理,从队列读取请求,并将(相关)响应发送到replyTo响应队列。最终在关联之后,响应将在HTTP响应中返回给调用者。

为了测试整个设置,我使用SoapUI作为测试驱动程序并在Stomp客户端中设置“假”请求持续时间(以模拟长请求处理时间)。

基本上我想实现以下目标:

  • 使用1个线程进行SoapUI加载测试60秒,使用模拟请求处理时间为1000毫秒的Stomp客户端:我预计有60个处理请求(平均请求时间超过1000毫秒),基本上没有驻留在代理中的消息
  • 使用2个线程进行SoaupUI负载测试60秒,Stomp客户端的模拟请求处理时间为1000毫秒:再次处理60个请求 BUT ,平均请求时间超过2000毫秒,应该始终是一个在代理中排队的消息(不是在传输缓冲区中的某个位置)等待另一个消息完成
  • 依旧......

我第一次尝试获得所需的行为是在Stomp客户端订阅时更改了prefetchSizedispatchAsync的ActiveMQ使用者的默认行为,如here所述。但是,默认的自动确认似乎在这里出现。由于消息以正确的方式被确认(不确定是什么时候),我几乎从未看到驻留在代理内部的消息(使用Java VisualVM进行检查);这些消息似乎在接收时就被承认了。这基本上可以反对 prefetchSize

那么我想使用客户端ACK,实际上是STOMP spec中描述的单个ACK。显然Camel Stomp组件不支持(至少我找不到)。所以我扩展了Apache Camel Stomp组件,以便在交换处理之后发送客户端ACK 。为此我实施并注册了一个定制的org.apache.camel.component.stomp.StompEndpoint。 ACK似乎工作正常,我也得到了预期的行为,即,一次只有Stomp客户端中只有一个消息,并且因为ACK仅在处理后发送(并且发送的响应),任何并行消息都驻留在代理中,直到(个人)确认。

但不幸的是,现在,当我以高负载测试这个场景时,我在org.apache.activemq.broker.region.PrefetchSubscription中遇到了死锁:

Found one Java-level deadlock:
=============================
"ActiveMQ Transport: tcp:///127.0.0.1:64896@25201":
  waiting to lock monitor 0x0000000026153878 (object 0x00000006c4dc0d90, a java.lang.Object),
  which is held by "ActiveMQ BrokerService[localhost] Task-2"
"ActiveMQ BrokerService[localhost] Task-2":
  waiting for ownable synchronizer 0x00000006c4db37f8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "ActiveMQ Transport: tcp:///127.0.0.1:64896@25201"

Java stack information for the threads listed above:
===================================================
"ActiveMQ Transport: tcp:///127.0.0.1:64896@25201":
    at org.apache.activemq.broker.region.PrefetchSubscription.dispatchPending(PrefetchSubscription.java:632)
    - waiting to lock <0x00000006c4dc0d90> (a java.lang.Object)
    at org.apache.activemq.broker.region.PrefetchSubscription.acknowledge(PrefetchSubscription.java:394)
    at org.apache.activemq.broker.region.AbstractRegion.acknowledge(AbstractRegion.java:528)
    at org.apache.activemq.broker.region.RegionBroker.acknowledge(RegionBroker.java:475)
    at org.apache.activemq.broker.BrokerFilter.acknowledge(BrokerFilter.java:89)
    at org.apache.activemq.broker.BrokerFilter.acknowledge(BrokerFilter.java:89)
    at org.apache.activemq.broker.TransactionBroker.acknowledge(TransactionBroker.java:276)
    at org.apache.activemq.broker.BrokerFilter.acknowledge(BrokerFilter.java:89)
    at org.apache.activemq.broker.TransportConnection.processMessageAck(TransportConnection.java:581)
    at org.apache.activemq.command.MessageAck.visit(MessageAck.java:245)
    at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:330)
    at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:194)
    at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:45)
    at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:301)
    at org.apache.activemq.transport.stomp.StompTransportFilter.sendToActiveMQ(StompTransportFilter.java:97)
    at org.apache.activemq.transport.stomp.ProtocolConverter.sendToActiveMQ(ProtocolConverter.java:202)
    at org.apache.activemq.transport.stomp.ProtocolConverter.onStompAck(ProtocolConverter.java:456)
    at org.apache.activemq.transport.stomp.ProtocolConverter.onStompCommand(ProtocolConverter.java:250)
    at org.apache.activemq.transport.stomp.StompTransportFilter.onCommand(StompTransportFilter.java:85)
    at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)
    at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:233)
    at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
    at java.lang.Thread.run(Thread.java:748)
"ActiveMQ BrokerService[localhost] Task-2":
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000006c4db37f8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at org.apache.activemq.transport.MutexTransport.oneway(MutexTransport.java:66)
    at org.apache.activemq.broker.TransportConnection.dispatch(TransportConnection.java:1486)
    at org.apache.activemq.broker.TransportConnection.processDispatch(TransportConnection.java:971)
    at org.apache.activemq.broker.TransportConnection.dispatchSync(TransportConnection.java:927)
    at org.apache.activemq.broker.region.PrefetchSubscription.dispatch(PrefetchSubscription.java:742)
    at org.apache.activemq.broker.region.PrefetchSubscription.dispatchPending(PrefetchSubscription.java:664)
    - locked <0x00000006c4dc0da0> (a java.lang.Object)
    - locked <0x00000006c4dc0d90> (a java.lang.Object)
    at org.apache.activemq.broker.region.PrefetchSubscription.add(PrefetchSubscription.java:159)
    at org.apache.activemq.broker.region.Queue.doActualDispatch(Queue.java:2121)
    at org.apache.activemq.broker.region.Queue.doDispatch(Queue.java:2069)
    at org.apache.activemq.broker.region.Queue.pageInMessages(Queue.java:2210)
    at org.apache.activemq.broker.region.Queue.iterate(Queue.java:1644)
    - locked <0x00000006c4dc21c0> (a java.lang.Object)
    at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:133)
    at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:48)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

有趣的是,如果我不在“main”应用程序中使用连接池,那么就不会出现此死锁,即与代理的vm://localhost连接。但是这会为发送给代理的每条消息创建一个新的生产者,并且速度极慢。使用带有池连接的org.apache.activemq.ActiveMQConnectionFactory工厂会大大加快速度,但会导致死锁。死锁也出现在上面提到的第一个负载测试中,其中没有并行请求。

(嵌入式)代理设置:

@Bean
public BrokerService embeddedBroker() throws Exception {
    final BrokerService embeddedBroker = new BrokerService();
    embeddedBroker.addConnector("stomp://0.0.0.0:" + this.configuration.getBrokerStompPort());
    return embeddedBroker;
}

@Bean(initMethod = "start", destroyMethod = "stop")
public PooledConnectionFactory activeMQPooledConnectionFactory() {
    final ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
            "vm://localhost?create=false");
    final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(connectionFactory);
    return pooledConnectionFactory;
}

@Bean
@DependsOn("embeddedBroker")
public ActiveMQComponent embeddedBrokerConnection() {
    final JmsConfiguration jmsConfiguration = new JmsConfiguration(this.activeMQPooledConnectionFactory());
    final ActiveMQComponent brokerConnection = new ActiveMQComponent();
    brokerConnection.setConfiguration(jmsConfiguration);
    this.camelContext.addComponent("activemq", brokerConnection);
    return brokerConnection;
}

现在的具体问题是:

  • 与代理的vm连接有问题,因为只有连接池出现死锁?
  • Camel Stomp组件的自定义可能会出现问题,例如:可能错误是由ACK或类似之前/之后收到的replyTo队列上的响应引起的(我没有在这里粘贴自定义,“问题”已经太长了)?
  • 是否有关于何时必须针对请求/响应发送Stomp ACK的规范?

0 个答案:

没有答案