【问题标题】:3x latency observed in ActiveMQ only for higher WAN latencies在 ActiveMQ 中观察到的 3 倍延迟仅适用于更高的 WAN 延迟
【发布时间】:2023-04-05 21:52:01
【问题描述】:

我正在对我的组织的 ActiveMQ 5.8.0 代理配置进行性能表征,重点关注仅包含两个代理的简单代理网络配置中代理之间的 WAN 延迟的影响。我可以访问一个 WAN 仿真器设备,它允许我在两个代理之间的网络链接的每个方向上独立更改模拟的 WAN 延迟,同时让所有其他网络连接(尤其是客户端与其本地代理之间的连接)保持 0 毫秒的标称延迟。

当 WAN 延迟约为 150 毫秒或更低时,我看到的端到端延迟正是我们根据 WAN 延迟所期望看到的,但对于更高的延迟,端到端延迟是三倍我们所期望的。我搜索了 Google、SO 和 AMQ 邮件列表档案,但没有找到任何人谈论过这种行为,也没有找到任何可以解释发生了什么的人。

ActiveMQ 设置:

  • 非持久消息。无事务,无持久性技术延迟等。
  • 两个代理,它们之间有 WAN 仿真器。此 WAN 仿真器不会影响客户端和代理之间的流量,只会影响两个代理之间的流量。它会产生模拟延迟,但不会产生其他网络效应(数据包丢失、数据包乱序等)。
  • 一个用于从生产者到消费者的消息队列,一个用于从消费者到生产者的响应的独占回复队列。
  • 连接到 Broker 1 的一个生产者在 InOut 模式中使用 Camel,尽可能快地生成 20KB 消息,然后在发送下一条消息之前消耗来自独占回复队列的响应。
  • 一个消费者,连接到 Broker 2,也使用 Camel 来消费来自生产者的消息并回复到专属回复队列。
  • 来自生产者的消息包含一个标头值,消费者在该标头值上有一个选择器。生产者产生的所有消息都被消费者消费。
  • 因为生产者使用(同步)Camel InOut 模式,所以在前一条消息的响应被消费之前不会产生下一条消息。这意味着预取缓冲区和生产者流量控制不是这里的一个因素。

由于我们的消息是非持久性的,我希望单向端到端延迟是该方向的 WAN 延迟加上一个小的(2-5 毫秒)常数用于处理、Camel 的开销等,往返端到端延迟是两个单向 WAN 延迟的总和加上大约两倍的小常数(大约 5-10 毫秒)。

相关配置片段:

ActiveMQ 配置文件,删除不相关的样板内容:

<beans boilerplateSchemaStuff>
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">boilerPlateStuff</bean>

    <broker xmlns="http://activemq.apache.org/schema/core"
            brokerName="${broker.name}"
            useJmx="true"
            persistent="false"
            schedulePeriodForDestinationPurge="60000"
            networkConnectorStartAsync="true"
            cacheTempDestinations="true"
            timeBeforePurgeTempDestinations="300000"
            allowTempAutoCreationOnSend="true">
        <destinationPolicy>
            <policyMap>
              <policyEntries>
                <policyEntry queue=">" producerFlowControl="true" memoryLimit="10mb">
                  <pendingQueuePolicy>
                    <vmQueueCursor/>
                  </pendingQueuePolicy>
                  <slowConsumerStrategy>
                    <abortSlowConsumerStrategy abortConnection = "true"/>
                  </slowConsumerStrategy>
                </policyEntry>
              </policyEntries>
            </policyMap>
        </destinationPolicy>

        <managementContext>
           <managementContext createConnector="false" />
        </managementContext>

        <networkConnectors>
          <networkConnector name="REMOTE_QUEUES" uri="${broker.remote.uri}"
             networkTTL="3" decreaseNetworkConsumerPriority="true"
             conduitSubscriptions="false" dynamicOnly="true" />
        </networkConnectors>

        <plugins>
          <statisticsBrokerPlugin/>
          <discardingDLQBrokerPlugin dropAll = "true" dropTemporaryTopics = "true"
             dropTemporaryQueues = "true"/>
        </plugins>

        <systemUsage>reasonable limits, not believed to cause this issue since producer flow control isn't occurring</systemUsage>

        <transportConnectors>
            <transportConnector name="openwire" uri="tcp://0.0.0.0:${tcp.port}"/>
            <transportConnector name="stomp" uri="stomp://0.0.0.0:${stomp.port}"/>
        </transportConnectors>
    </broker>

    <import resource="file:${ACTIVEMQ_CONFIG_SC}/conf/jetty.xml"/>
</beans>

生产者 Spring 配置:

<beans boilerplateSchemaStuff>
   <bean id="messageGen" class="teststubs.MessageGenerator">
      <property name="name" value="MessageGenerator"/>
   </bean>

   <bean id="amqMessageThroughputProcessor" class="teststubs.AmqMessageThroughputProcessor">
      <property name="name" value="QueueResponseConsumer${token}"/>
   </bean>

   <bean id="token" class="java.lang.String">
      <constructor-arg value="${token}" />
   </bean>

   <camel:camelContext allowUseOriginalMessage="false">
      <camel:jmxAgent id="agent" disabled="true" />

      <camel:endpoint id="sendDestination"
         uri="jms:${destination.type}:PerfTest#{T(org.apache.commons.lang3.text.WordUtils).capitalize('${destination.type}')}?requestTimeout=10000&amp;replyTo=PerfTestResponseQueue${token}&amp;replyToType=Exclusive&amp;concurrentConsumers=1&amp;mapJmsMessage=false"
         xmlns="http://camel.apache.org/schema/spring"/>

      <camel:route>
         <camel:from uri="timer://sendTask?period=1&amp;fixedRate=true&amp;repeatCount=1" />
         <camel:loop copy="true">
            <camel:constant>10000</camel:constant>
            <camel:bean ref="messageGen" method="create20KbTimestampedMessage"/>
            <camel:setHeader headerName="Token">
               <camel:simple>ref:token</camel:simple>
            </camel:setHeader>
            <camel:to ref="sendDestination" pattern="InOut" />
            <camel:process ref="amqMessageThroughputProcessor" />
         </camel:loop>
      </camel:route>
   </camel:camelContext>
</beans>

Consumer Spring 配置:

<beans boilerplateSchemaStuff>
   <bean id="amqMessageThroughputProcessor" class="teststubs.AmqMessageThroughputProcessor">
      <property name="name" value="QueueConsumer"/>
   </bean>

   <camel:camelContext>
      <camel:jmxAgent id="agent" disabled="true" />

      <camel:endpoint id="receiveDestination"
         uri="jms:${destination.type}:PerfTest#{T(org.apache.commons.lang3.text.WordUtils).capitalize('${destination.type}')}?concurrentConsumers=1&amp;mapJmsMessage=false&amp;selector=Token='${token}'"
         xmlns="http://camel.apache.org/schema/spring"/>

      <camel:route streamCache="true">
         <camel:from ref="receiveDestination" />
         <camel:process ref="amqMessageThroughputProcessor" />
      </camel:route>
   </camel:camelContext>
</beans>

观察结果:

两个方向的延迟都很低(

进一步改变 WAN 延迟(保持在 150 毫秒以上的单向延迟)会立即改变延迟以保持 (3 x one-way WAN latency) + 5ms 的单向端到端延迟和往返端到端延迟的(3 x (forward one-way WAN latency + return one-way WAN latency) + 10ms。由于延迟的增加似乎与 WAN 延迟的变化成正比,因此可以肯定地说,它是由跨网络的东西引起的,而不是在生产者、消费者或两者中引入(固定)延迟的东西经纪人。

因为我可以独立地改变两个方向上的模拟延迟,我已经能够确定当两个方向具有相同延迟时我看到的 3 倍单向延迟实际上是 (2 x forward one-way WAN latency) + (1 x return one-way WAN latency) 用于正向一个-way 端到端延迟,与1 x forward one-way WAN latency 的预期值相比。减去这两者表明意外的额外延迟是(1 x forward one-way WAN latency) + (1 x return one-way WAN latency),这表明在代理之间发生的每条消息都有一个意外的往返(可能也在每个代理与其客户端之间?)。响应消息也发现了相同的结果。

可能的原因:

ping 报告的两个代理主机之间的往返时间为 300 毫秒(如预期的那样),因此我没有任何理由相信这是由于 WAN 模拟器引入了错误的模拟延迟造成的,不过如果出于某种原因无法信任 ping 报告的 RTT,我愿意尝试进一步探索。但在没有具体理由不信任广泛使用的 ping 实用程序的情况下,这似乎不是问题。 更新:我使用了一个以前编写和测试过的网络流量工具来确认 TCP 数据包在1 x forward one-way WAN latency 中的一个方向传递,即使 ActiveMQ TCP 数据包需要额外的往返.我相信这进一步表明 WAN 模拟器没有引入这种行为。

我也没有任何理由相信任何有意义的延迟部分源于 Camel,因为将 WAN 仿真器设置为双向 0 毫秒延迟,我每秒可以推送约 200 条消息。我相信 Camel 也不会在代理之间的网络接口上做任何事情(Camel 只会通过发送 ActiveMQ 消息来访问该网络链接,而 ActiveMQ Web 控制台没有显示发送其他消息的证据),但在这里,如果有人知道具体的事情表明 Camel 可能导致了这种行为,我愿意对此进行调查。

我最初的猜测是消息正在被重新传输,但我看不到任何证据;当延迟增加三倍时,WAN 模拟器的吞吐量下降到其原始值的 1/3 以上,而如果消息在代理之间重新传输,我希望它至少是原始值的 2/3(1/ 3 用于第一次传输,1/3 用于第二次传输,加上少量用于重传请求消息的大小)。我还希望 Web 控制台上的 Dequeue Count 大于 Enqueue Count,但这也没有发生。所以我也不相信这就是正在发生的事情(即使是这样,它也无法解释为什么它会在某些延迟而不是其他延迟中发生,也无法解释为什么对于给定的延迟它会开始没有发生然后突然开始发生)。

有没有人知道什么可能导致这种行为,或者如果我必须阅读源代码以寻找线索,那么我最好的起点是什么 ActiveMQ 代码?

【问题讨论】:

  • 我没有真正的想法,但有趣的是activemq.apache.org/optimized-acknowledgement.html 谈到了默认的 300 毫秒的 optimizeAcknowledgeTimeOut,即双倍的单向延迟,您开始看到问题。
  • @matthelliwell 我认为这些数字的关系只是巧合。我没有进行批量确认(一次只有一条消息在队列中,因为 Camel 在收到对前一条消息的响应之前不会发送下一条消息,因此批量确认不会做任何事情并且不是t 默认值),昨天的进一步测试表明该行为发生的时间低至 95 毫秒(尽管在该延迟下,它通常会在几秒到几分钟后自行纠正,而在 150 毫秒时它只是保持中断)。不过谢谢你的建议。

标签: java apache-camel activemq


【解决方案1】:

结果证明,这纯粹是在具有高带宽延迟产品的高延迟 WAN 上调整 TCP 的问题。我们的代理到代理的连接是单工链接而不是双工链接(因此我们可以为两个代理使用相同的配置文件),因此在发送同步请求-响应消息时,一个链接始终处于空闲状态,而另一个正在使用中。

默认情况下,Linux 会主动收缩空闲 TCP 连接的拥塞窗口(对于每个空闲的 RTT,将 cwnd 值减半),这意味着 TCP 窗口会在每条消息发送时打开,然后收缩回来在等待在另一个 TCP 连接上发送响应时,下降到 initcwnd(=3,在 3.2 之前的内核中)。结果,拥塞窗口永远不会打开,所以我们一次只能有几个数据包在传输,这意味着我们必须为每 20KB 消息等待一个额外的 RTT(每 80KB 需要额外的 4-5 个 RTT信息)。因此,我观察到的 3 倍延迟纯属巧合,具体取决于我正在测试的消息的大小,而且我观察到较大消息的乘数不同。

https://serverfault.com/questions/386834/clarification-about-linux-tcp-window-size-and-delays 基本上描述了相同的问题(尽管 OP 使用非常小的消息,并且一些答案锁定在该问题上而不是 TCP 连接空闲;关注 OP 自己接受的答案而不是其他答案),如以及解决方案:通过sysctl -w net.ipv4.tcp_slow_start_after_idle=0 告诉内核不要在空闲期间更改拥塞窗口。

该问题还描述了我发现查看 TCP 套接字当前 cwnd 值的唯一方法:/usr/sbin/ss -i -t -e。关于ss 命令输出的文档非常缺乏(例如,我找不到任何准确描述rcv_rtt 含义的内容),但该实用程序本身对于确认我的拥塞窗口确实正在关闭非常有用正如我所怀疑的那样。

【讨论】:

    猜你喜欢
    • 2020-08-09
    • 2017-01-01
    • 2019-02-02
    • 1970-01-01
    • 2017-07-05
    • 2021-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多