Artemis 提供了一些基于 JMS 2 API 的内置性能测试工具,帮助用户(和开发人员)在不同的场景中对配置的 Artemis 代理实例进行压力测试。

这些命令行工具不会代表一个完整的基准测试(例如 Open Messaging),但可以用作构建块来生成一个基准测试。它们本身也很有用。

总之,提供的 perf 工具是

  1. 生产者 工具:它可以使用配置大小的 BytesMessage 生成全速吞吐量或目标速率负载

  2. 消费者 工具:它使用 MessageListener 消费由 生产者 命令发送的消息

  3. 客户端 工具:它将这两个工具打包成一个命令

大多数用户只需要 客户端 工具,但 生产者消费者 工具允许在其他场景中执行测试

  • 延迟消费者启动,以导致代理分页

  • 在不同机器上运行生产者和消费者

  • …​

下面的示例(在 64 位 Linux 5.14 上运行,配备 Intel® Core™ i7-9850H CPU @ 2.60GHz × 12,Turbo Boost 禁用,32 GB RAM 和 SSD)展示了复杂度不断增加的不同用例。随着示例的进行,将探讨该工具的一些内部架构细节以及支持的配置选项。

这些工具既可以在代理实例文件夹内运行,也可以在主文件夹中运行。在这两种情况下,用户都应设置 JAVA_ARGS 环境变量以覆盖默认的堆和 GC 参数(例如 -XX:+UseParallelGC -Xms512M -Xmx1024M

1. 案例 1:队列上的单个生产者单个消费者

这是最简单的用例:在非持久队列 TEST_QUEUE 上运行一个负载测试,使用 非持久 的默认大小为 1024 字节的消息,使用 自动确认

让我们看看输入以下内容后会发生什么

$ ./artemis perf client queue://TEST_QUEUE
Connection brokerURL = tcp://127.0.0.1:61616
2022-01-18 10:30:54,535 WARN  [org.apache.activemq.artemis.core.client] AMQ212053: CompletionListener/SendAcknowledgementHandler used with confirmationWindowSize=-1. Enable confirmationWindowSize to receive acks from server!
--- warmup false
--- sent:          7316 msg/sec
--- blocked:       6632 msg/sec
--- completed:     7320 msg/sec
--- received:      7317 msg/sec
# ...

测试会一直运行,直到向 Java 进程发送 SIGTERMSIGINT 信号(在 Linux 控制台上,这相当于按下 CTRL + C)。在查看指标的含义之前,有一个初始的 WARN 日志,不应忽略

WARN  [org.apache.activemq.artemis.core.client] AMQ212053: CompletionListener/SendAcknowledgementHandler used with confirmationWindowSize=-1. Enable confirmationWindowSize to receive acks from server!

它显示了两个内容

  1. 负载生成器使用 异步消息生产者

  2. confirmationWindowSize 是 Artemis CORE 协议的特定设置;perf 命令使用 CORE 作为默认的 JMS 提供程序

1.1. 实时延迟控制台报告

perf client 命令可以通过在命令参数中添加 --show-latency 来在控制台上报告不同的延迟百分位数指标,但为了获得有意义的指标,我们需要通过在生产者 url 上设置 confirmationWindowSize 来解决 WARN,并设置 --consumer-url 以保存对消费者应用相同配置。

简而言之,该命令使用以下附加参数

--show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616

1.1.1. 运行它

$ ./artemis perf client --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 queue://TEST_QUEUE
--- warmup false
--- sent:          8114 msg/sec
--- blocked:       8114 msg/sec
--- completed:     8114 msg/sec
--- received:      8113 msg/sec
--- send ack time:   mean:    113.01 us - 50.00%:    106.00 us - 90.00%:    142.00 us - 99.00%:    204.00 us - 99.90%:    371.00 us - 99.99%:   3455.00 us - max:      3455.00 us
--- transfer time:   mean:    213.71 us - 50.00%:    126.00 us - 90.00%:    177.00 us - 99.00%:   3439.00 us - 99.90%:   7967.00 us - 99.99%:   8895.00 us - max:      8895.00 us
# CTRL + C pressed
--- SUMMARY
--- result:              success
--- total sent:            70194
--- total blocked:         70194
--- total completed:       70194
--- total received:        70194
--- aggregated send time:       mean:    101.53 us - 50.00%:     86.00 us - 90.00%:    140.00 us - 99.00%:    283.00 us - 99.90%:    591.00 us - 99.99%:   2007.00 us - max:     24959.00 us
--- aggregated transfer time:   mean:    127.48 us - 50.00%:     97.00 us - 90.00%:    166.00 us - 99.00%:    449.00 us - 99.90%:   4671.00 us - 99.99%:   8255.00 us - max:     27263.00 us

一些说明

  1. WARN 消息现在消失了

  2. 发送确认时间传输时间 统计信息以每秒间隔打印

  3. 总计聚合 指标在测试完成后打印(稍后会详细介绍)

实时延迟统计信息的含义是

发送确认时间

确认已发送消息的延迟的百分位数

传输时间

从生产者到消费者的消息传输延迟的百分位数

perf 命令使用 JMS 2 异步消息生产者,允许负载生成器累积正在传输的已发送消息,并且根据协议的实现,可能会由于生产者流量控制而阻塞其生产者线程。例如:Artemis CORE 协议可能会阻塞生产者线程以重新填充生产者信用,而 QPID-JMS 则不会。

perf 工具实现了它自己的正在传输的已发送请求跟踪,并且可以配置为限制正在传输的已发送消息的数量,同时报告生产者因等待完成而“阻塞”的速率

生产者线程被“阻塞”了?

尽管负载反压机制是非阻塞的,但鉴于负载生成器在受到协议客户端反压的情况下无法推送更多负载,因此负载在语义上是“阻塞”的。此细节与解释控制台上的实时速率 统计信息 相关

默认情况下,perf 工具(即:clientproducer将正在传输的请求数量限制为 1:要更改默认设置,用户应添加 --max-pending 参数配置。

设置 --max-pending 0 将禁用负载生成器正在传输的已发送消息限制器,允许该工具累积无限数量的正在传输的消息,从而有 OutOfMemoryError 的风险。

不建议这样做!

有关指标的更多详细信息

预热

收集统计信息样本时的生成阶段;预热时间可以通过设置 --warmup 来设置

已发送

已发送消息的速率

阻塞

尝试发送新消息的速率,因等待 --max-pending 重新填充而“阻塞”

已完成

生产者收到的消息发送确认的速率

已接收

消费者收到的消息的速率

1.2. 如何读取实时统计信息?

+ 阻塞已发送 之间的巨大差异意味着代理无法在发送新消息之前足够快地重新填充单个 --max-pending 预算。+ 它可以更改为

--max-pending 100
$ ./artemis perf client --warmup 20 --max-pending 100 --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 queue://TEST_QUEUE
Connection brokerURL = tcp://127.0.0.1:61616?confirmationWindowSize=20000
# first samples shows very BAD performance because client JVM is still warming up
--- warmup true
--- sent:         27366 msg/sec
--- blocked:        361 msg/sec
--- completed:    27305 msg/sec
--- received:     26195 msg/sec
--- send ack time:   mean:   1743.39 us - 50.00%:   1551.00 us - 90.00%:   3119.00 us - 99.00%:   5215.00 us - 99.90%:   8575.00 us - 99.99%:   8703.00 us - max:     23679.00 us
--- transfer time:   mean:  11860.32 us - 50.00%:  11583.00 us - 90.00%:  18559.00 us - 99.00%:  24319.00 us - 99.90%:  31359.00 us - 99.99%:  31615.00 us - max:     31615.00 us
# ... > 20 seconds later ...
# performance is now way better then during warmup
--- warmup false
--- sent:         86525 msg/sec
--- blocked:       5734 msg/sec
--- completed:    86525 msg/sec
--- received:     86556 msg/sec
--- send ack time:   mean:   1109.13 us - 50.00%:   1103.00 us - 90.00%:   1447.00 us - 99.00%:   1687.00 us - 99.90%:   5791.00 us - 99.99%:   5983.00 us - max:      5983.00 us
--- transfer time:   mean:   4662.94 us - 50.00%:   1679.00 us - 90.00%:  12159.00 us - 99.00%:  14079.00 us - 99.90%:  14527.00 us - 99.99%:  14783.00 us - max:     14783.00 us
# CTRL + C
--- SUMMARY
--- result:              success
--- total sent:          3450389
--- total blocked:        168863
--- total completed:     3450389
--- total received:      3450389
--- aggregated send time:       mean:   1056.09 us - 50.00%:   1003.00 us - 90.00%:   1423.00 us - 99.00%:   1639.00 us - 99.90%:   4287.00 us - 99.99%:   7103.00 us - max:     19583.00 us
--- aggregated transfer time:   mean:  18647.51 us - 50.00%:  10751.00 us - 90.00%:  54271.00 us - 99.00%:  84991.00 us - 99.90%:  90111.00 us - 99.99%:  93183.00 us - max:     94207.00 us

关于结果的一些说明

  • 我们现在有了合理的 阻塞/已发送 比例(< ~10%)

  • 之前的结果 相比,已发送速率提高了 十倍

关于 SUMMARY 统计信息

  • 总计 计数器包括使用 warmup true 收集的度量

  • 聚合 延迟包括使用 warmup true 收集的度量

1.3. 如何比较不同测试的延迟?

控制台输出格式并非为轻松比较延迟而设计的,但是 perf 命令公开 --hdr <hdr 文件名> 参数以生成 HDR Histogram 兼容的报告,可以使用不同的可视化工具打开,例如 在线 HdrHistogram 日志分析器HdrHistogramVisualizerHistogramLogAnalyzer

本指南中收集的任何延迟跟踪都将使用 在线 HdrHistogram 日志分析器 作为 HDR Histogram 可视化工具。

以下是将之前的基准测试添加到基准测试中时收集的 HDR 直方图的可视化效果

--hdr /tmp/non_durable_queue.hdr

整个测试执行显示标记的延迟,以区分 预热 延迟

test

过滤掉 预热 延迟,它看起来像

hot test

延迟结果表明,在更高的百分位数上,传输 延迟远高于 发送 延迟(提醒:发送 是确认已发送消息的时间),这可能意味着代理上正在发生一些排队。

为了验证这个理论,我们切换到目标速率测试

2. 案例 2:目标速率队列上的单个生产者单个消费者

perf clientperf producer 工具允许指定目标速率以调度生产者请求:添加

--rate <msg/sec integer value>

前面的示例 上次运行 表明 --max-pending 100 保证 < 10% 的阻塞/已发送消息,且聚合延迟

--- aggregated send time:       mean:   1056.09 us - 50.00%:   1003.00 us - 90.00%:   1423.00 us - 99.00%:   1639.00 us - 99.90%:   4287.00 us - 99.99%:   7103.00 us - max:     19583.00 us
--- aggregated transfer time:   mean:  18647.51 us - 50.00%:  10751.00 us - 90.00%:  54271.00 us - 99.00%:  84991.00 us - 99.90%:  90111.00 us - 99.99%:  93183.00 us - max:     94207.00 us

我们希望将 传输时间 降至毫秒级以下;让我们尝试通过运行一个负载测试,以最大感知发送速率的约 30% 的速率来运行测试,方法是设置

--rate 30000 --hdr /tmp/30K.hdr

然后整个命令是

$ ./artemis perf client --rate 30000 --hdr /tmp/30K.hdr --warmup 20 --max-pending 100 --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 queue://TEST_QUEUE
# ... after 20 warmup seconds ...
--- warmup false
--- sent:         30302 msg/sec
--- blocked:          0 msg/sec
--- completed:    30302 msg/sec
--- received:     30303 msg/sec
--- send delay time: mean:     24.20 us - 50.00%:     21.00 us - 90.00%:     54.00 us - 99.00%:     72.00 us - 99.90%:    233.00 us - 99.99%:    659.00 us - max:       731.00 us
--- send ack time:   mean:    150.48 us - 50.00%:    120.00 us - 90.00%:    172.00 us - 99.00%:   1223.00 us - 99.90%:   2543.00 us - 99.99%:   3183.00 us - max:      3247.00 us
--- transfer time:   mean:    171.53 us - 50.00%:    135.00 us - 90.00%:    194.00 us - 99.00%:   1407.00 us - 99.90%:   2607.00 us - 99.99%:   3151.00 us - max:      3183.00 us
# CTRL + C
--- SUMMARY
--- result:              success
--- total sent:          1216053
--- total blocked:           845
--- total completed:     1216053
--- total received:      1216053
--- aggregated delay send time: mean:     35.84 us - 50.00%:     20.00 us - 90.00%:     55.00 us - 99.00%:    116.00 us - 99.90%:   3359.00 us - 99.99%:   5503.00 us - max:      6495.00 us
--- aggregated send time:       mean:    147.38 us - 50.00%:    117.00 us - 90.00%:    165.00 us - 99.00%:    991.00 us - 99.90%:   4191.00 us - 99.99%:   5695.00 us - max:      7103.00 us
--- aggregated transfer time:   mean:    178.48 us - 50.00%:    134.00 us - 90.00%:    188.00 us - 99.00%:   1359.00 us - 99.90%:   5471.00 us - 99.99%:   8831.00 us - max:     12799.00 us

我们现在已经实现了直到 90.00 百分位数 的毫秒级以下 传输 延迟。+ 打开 /tmp/30K.hdr 可以更轻松地看到它

test

现在 发送传输 时间看起来非常相似,并且没有排队的迹象,但是…​

2.1. 延迟发送时间 的含义是什么?

此指标借鉴了 协同遗漏 概念,它衡量生产者在尝试以请求的速率发送消息时的延迟。

这种延迟的来源可能是

  • 响应缓慢的代理:负载生成器达到了 --max-pending,无法满足预期的速率

  • 客户端资源不足(缺少 CPU 时间、GC 暂停等):负载生成器无法跟上预期的速率,因为它对客户端来说“太快”了

  • 协议相关的阻塞行为:CORE JMS 2 异步发送可能会因 producerWindowSize 耗尽而阻塞

目标速率测试的正常运行应将 延迟发送时间 控制在一定范围内,或者必须采取调查措施以了解延迟的来源。+ 让我们以一个例子来说明:我们已经检查了代理的全速率,即约 90K 条消息/秒

通过在相同条件下运行 --rate 90000 测试,延迟将如下所示

test

它清楚地表明,负载生成器正在延迟,并且无法跟上预期的速率。

以下是一个更复杂的示例,涉及使用“非对称”负载(即:生产者数量与消费者数量不同)自动生成目的地。

3. 案例 3:10 个持久主题上的目标速率负载,每个主题有 3 个生产者和 2 个非共享消费者

perf 工具可以使用以下命令自动生成目的地

--num-destinations <number of destinations to generate>

并使用指定为种子的目的地名称和有序序列后缀对其进行命名。

例如

--num-destinations 3 topic://TOPIC

将生成 3 个主题:TOPIC0TOPIC1TOPIC2

使用默认配置(不指定 --num-destinations)将只创建 TOPIC,没有任何数字后缀。

为了在 10 个主题上创建负载生成,每个主题有 3 个生产者和 2 个非共享消费者

--producers 3 --consumers 2 --num-destinations 10 topic://TOPIC

整个 perf client 全速吞吐量命令将是

# same as in the previous cases
./artemis perf client --warmup 20 --max-pending 100 --s
how-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 \
--producers 3 --consumers 2 --num-destinations 10 --durable --persistent topic://DURABLE_TOPIC
# this last part above is new

它将打印…​

javax.jms.IllegalStateException: Cannot create durable subscription - client ID has not been set

鉴于生成器正在创建 非共享持久主题订阅,因此必须为使用的每个连接设置 ClientID。

默认情况下,perf client 工具会为每个消费者创建一个连接,并自动生成 ClientID 和订阅名称(根据 非共享持久化主题订阅 API 的要求)。ClientID 仍然需要用户使用 --clientID <ClientID 前缀> 指定 Client ID 前缀,并在测试完成后取消订阅消费者。

完整的命令现在看起来像这样

./artemis perf client --warmup 20 --max-pending 100 --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 \
--producers 3 --consumers 2 --num-destinations 10 --durable --persistent topic://DURABLE_TOPIC --clientID test_id
# after few seconds
--- warmup false
--- sent:         74842 msg/sec
--- blocked:       2702 msg/sec
--- completed:    74641 msg/sec
--- received:    146412 msg/sec
--- send ack time:   mean:  37366.13 us - 50.00%:  37119.00 us - 90.00%:  46079.00 us - 99.00%:  68095.00 us - 99.90%:  84479.00 us - 99.99%:  94719.00 us - max:     95743.00 us
--- transfer time:   mean:  44060.66 us - 50.00%:  43263.00 us - 90.00%:  54527.00 us - 99.00%:  75775.00 us - 99.90%:  87551.00 us - 99.99%:  91135.00 us - max:     91135.00 us
# CTRL + C
--- SUMMARY
--- result:              success
--- total sent:          2377653
--- total blocked:         80004
--- total completed:     2377653
--- total received:      4755306
--- aggregated send time:       mean:  39423.69 us - 50.00%:  38911.00 us - 90.00%:  49663.00 us - 99.00%:  66047.00 us - 99.90%:  85503.00 us - 99.99%: 101887.00 us - max:    115711.00 us
--- aggregated transfer time:   mean:  46216.99 us - 50.00%:  45311.00 us - 90.00%:  57855.00 us - 99.00%:  78335.00 us - 99.90%:  97791.00 us - 99.99%: 113151.00 us - max:    125439.00 us

结果表明 传输时间 没有排队,这意味着订阅者能够跟上生产者:因此一个合理的测试速率可以是感知到的 发送 速率的约 80%,即 --rate 60000

./artemis perf client --warmup 20 --max-pending 100 --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 \
--producers 3 --consumers 2 --num-destinations 10 --durable --persistent topic://DURABLE_TOPIC --clientID test_id \
--rate 60000
# after many seconds while running
--- warmup false
--- sent:         55211 msg/sec
--- blocked:       2134 msg/sec
--- completed:    54444 msg/sec
--- received:    111622 msg/sec
--- send delay time: mean: 6306710.04 us - 50.00%: 6094847.00 us - 90.00%: 7766015.00 us - 99.00%: 8224767.00 us - 99.90%: 8257535.00 us - 99.99%: 8257535.00 us - max:    8257535.00 us
--- send ack time:   mean:  50072.92 us - 50.00%:  50431.00 us - 90.00%:  57855.00 us - 99.00%:  65023.00 us - 99.90%:  71167.00 us - 99.99%:  71679.00 us - max:     71679.00 us
--- transfer time:   mean:  63672.92 us - 50.00%:  65535.00 us - 90.00%:  78847.00 us - 99.00%:  86015.00 us - 99.90%:  90623.00 us - 99.99%:  93183.00 us - max:     94719.00 us
# it won't get any better :(

发送延迟时间 出了什么问题?+ 结果表明,负载生成器无法跟上预期速率,并且在预期的计划负载上积累了巨大的延迟:让我们尝试通过添加更多生产者线程来解决这个问题,添加

--threads <producer threads>

使用两个生产者线程,命令现在看起来像这样

./artemis perf client --warmup 20 --max-pending 100 --show-latency --url tcp://127.0.0.1:61616?confirmationWindowSize=20000 --consumer-url tcp://127.0.0.1:61616 \
--producers 3 --consumers 2 --num-destinations 10 --durable --persistent topic://DURABLE_TOPIC --clientID test_id \
--rate 60000 --threads 2
# after few seconds warming up....
--- warmup false
--- sent:         59894 msg/sec
--- blocked:        694 msg/sec
--- completed:    58925 msg/sec
--- received:    114857 msg/sec
--- send delay time: mean:   3189.96 us - 50.00%:    277.00 us - 90.00%:  10623.00 us - 99.00%:  35583.00 us - 99.90%:  47871.00 us - 99.99%:  56063.00 us - max:     58367.00 us
--- send ack time:   mean:  31500.93 us - 50.00%:  31231.00 us - 90.00%:  48383.00 us - 99.00%:  65535.00 us - 99.90%:  83455.00 us - 99.99%:  95743.00 us - max:     98303.00 us
--- transfer time:   mean:  38151.21 us - 50.00%:  37119.00 us - 90.00%:  55807.00 us - 99.00%:  84479.00 us - 99.90%: 104959.00 us - 99.99%: 118271.00 us - max:    121855.00 us

发送延迟时间 现在似乎得到了控制,这意味着负载生成器需要进行一些调整才能发挥最佳性能。