RabbitMQ PerfTest Version 2.20.0
https://perftest.rabbitmq.com/
PerfTest是一个用于RabbitMQ的吞吐量测试工具。
介绍
RabbitMQ有一个基于Java客户端的吞吐量测试工具,其可以模拟基本工作负载和高级工作负载。PerfTest 还有额外的工具,可以生成 HTML 输出图。
RabbitMQ集群会受限于众多因素,从底层限制(例如网络带宽)到RabbitMQ配置,以及生产者和消费者拓扑结构。
PerfTest可以揭示单个节点或者集群节点的基准性能。
PerfTest使用AMQP 0.9.1协议与RabbitMQ集群进行通信,若你想要用stream protocol测试RabbitMQ Streams,使用Stream PerTest。
安装
二进制
PerfTest以uber JAR形式从GitHub releases发布。
WARNNING
tar.gz和zip包已被弃用并且将在PerfTest3.0中移除,由JAR取代。
若作为库使用可以从Maven仓库获取。
验证PerfTest安装,使用:
java -jar perf-test.jar --help
Docker Image
PerfTest也有Docker镜像,使用:
docker run -it --rm pivotalrabbitmq/perf-test:latest --help
注意Docker需要能够连接到broker所在主机,详见Docker network documentation。只要运行PerfTest的Docker容器能够连接到RabbitMQ broker,PerfTest可以使用一般参数运行,例如:
docker run -it --rm pivotalrabbitmq/perf-test:latest -x 1 -y 2 -u "throughput-test-1" -a --id "test 1"
要在 Docker 中运行 RabbitMQ 代理并对其运行 PerfTest,请运行以下命令:
docker network create perf-test
docker run -it --rm --network perf-test --name rabbitmq -p 15672:15672 rabbitmq:3.12-management
docker run -it --rm --network perf-test pivotalrabbitmq/perf-test:latest --uri amqp://rabbitmq
基本用法
运行 PerfTest 的最基本方法仅指定一个要连接的 URI、要使用的发布者数量(如 1)和要使用的消费者数量(如 2)。请注意,只要有足够的带宽,并禁用了一些安全措施(发布者确认),RabbitMQ Java 客户端就能实现很高的发布速率(每个连接每秒最多80至90K条消息),因此很少有必要过度配置发布者(除非这是测试的一个特定目标)。
下面的命令运行PerfTest,其中包含一个无发布者确认的发布者、两个使用自动确认模式的消费者(每个消费者接收每条消息的副本)和一个名为 “throughput-test-x1-y2 “的队列。发布者将尽可能快地发布信息,没有任何速率限制。结果将以 “test1 “为前缀,以便于识别和比较:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-1" -a --id "test 1"
这次使用两个生产者和4个消费者,给这台机器或者RabbitMQ足够的核心数能够产生较高的吞吐量。
java -jar perf-test.jar -x 2 -y 4 -u "throughput-test-2" -a --id "test 2"
这次切换消费者为手动确认:
java -jar perf-test.jar -x 1 -y 2 -u "throught-test-3" --id "test 3"
修改消息大小,从默认(12字节)到4kb:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-4" --id "test 4" -s 4000
PerfTest可以使用持久队列和持久消息:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-5" --id "test-5" -f persistent
当PerfTest运行时,监控management UI提供的各种生产者和消费者数据很重要。例如,在connection页面查看一个消费者最近使用了多少网络带宽。
Queue页面显示消息速率、消费者计数、消费者使用的确认模式、消费者利用率和消息位置细分(磁盘、内存、分页暂存信息等)。当使用持久队列和持久消息时,节点 I/O 和消息存储/队列索引操作指标的监控就变得尤为重要。
消费者可以一次性ack多个消息,例如,配置100个:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-6" --id "test-6" -f persistent --multi-ack-every 100
消费者prefetch(QoS)同样可以配置(例如500):
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-7" --id "test-7" -f persistent --multi-ack-every 200 -q 500
发布者确认最多可使用 N 个未发布的发布者:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-8" --id "test-8" -f persistent -q 500 -c 500
PerfTest可以发布确切数量的消费而非运行到关闭:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-10" --id "test-10" -f persistent -q 500 -pmessages 100000
可以限制发布者速率:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-11" --id "test-11" -f persistent -q 500 --rate 5000
同样能限制消费者速率以模拟缓慢消费者或者创建消息堆积:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-12" --id "test-12" -f persistent --rate 5000 --consumer-rate 2000
注意消费者速率限制应用于每一个消费者,故上述配置中限制实际上是2 * 2000 = 4000次/秒。
PerfTest 会自动将低发布率(每秒 1 到 10 条信息)转换为发布间隔。这样,在模拟许多慢速发布者时,模拟结果会更加准确。
可以使用 -z 选项将 PerfTest 配置为以秒为单位的有限运行时间:
java -jar perf-test.jar -x 1 -y 2 -u "throughput-test-13" --id "test-13" -f persistent -z 30
没有消费者但指定有限消息运行PerfTest可以用于预填充队列,例如,单个1kb共计百万消息的队列:
java -jar perf-test.jar -y0 -p -u "throughput-test-14" -s 1000 -C 1000000 --id "test-14" -f persistent
使用-D选项限制消费的消息数量。注意-Z(时间限制),-C(发布的消息数量),以及-D(消费的消息数量)可以被同时使用,但可能导致滑稽的结果。-r 1 -x 1 -C 10 -y 1 -D 20是一个例子,一旦生产者发布10条消息就会停止,让消费者永远等待剩下的10条消息(因为生产者停止了)。
不使用任何生产者,仅从预先声明和预先填充的队列中消费消息,使用:
java -jar perf-test.jar -x0 -y10 -p -u "throughput-test-14" --id "test-15"
PerfTest 对于使用各种配置建立基线群集吞吐量非常有用,但并不能模拟真实世界应用程序的许多其他方面。它还偏向于使用单个队列的非常简单的工作负载,这使得 RabbitMQ 节点上的 CPU 利用率有限,在大多数情况下并不推荐使用。
同时运行多个 PerfTest 实例可用于模拟更真实的工作负载。
工作原理
若队列名称被定义(-u "queue-name"),PerfTest会以该名字创建一个队列,所有消费者将从该队列消费。队列会被绑定到direct交换机上,以队列名称为路由键。该路由键用于生产者发送消息。这样从所有所有消费者来的消息会被发送到一个队列,所有消费者从该队列接收消息。
若队列名称未定义,PerfTest会创建一个随机UUID路由键,生产者用其发送消息。每个消费者会创建其自己的匿名队列并且绑定到direct交换机,路由键为队列名称。这样从所有生产者来的消息会复制到多个队列(队列数等于消费者数),每个消费者仅会从一个队列接收消息。
请注意,可以自定义队列,也可以针对多个队列工作。
停止PerfTest
有两个原因要停止PerfTest运行:
- 一是达到限制(时间限制、生产者或消费者消息数量)
- 进程被用户停止,例如,终端中使用Ctrl C
以上两者,PerfTest会在合理时间内退出并尽可能清理资源。然而,当PerfTest的AMQP连接被broker限流,因为PerfTest发布太快或者broker告警启动,这就需要花点时间关闭连接(每个连接数秒或者更多)。
如果以温和的方式关闭连接耗时过长(默认情况下为 5 秒),PerfTest 将继续释放最重要的资源并终止。这会导致代理日志中出现客户端意外关闭 TCP 连接的消息。请注意,这意味着 AMQP 连接没有以正确的 AMQP 帧序列关闭,但套接字已正确关闭。这里没有资源泄漏。
连接关闭超时时间可以使用--shutdow-timeout(或者-st)参数设置。可以增加默认超时时间以有更多事件来关闭连接,例如,以下命令使用20s超时时间:
java -jar perf-test.jar --shutdown-timeout 20
也可以通过将超时设置为 0 或任何负值来跳过连接关闭序列:
java -jar perf-test.jar --shutdown-timeout -1
使用前一条命令时,PerfTest 甚至不会尝试关闭 AMQP 连接,而是尽可能快地退出,只释放最重要的资源。在测试环境中运行时,这是完全可接受的。
自定义队列
PerfTest可以使用给定的参数创建队列:
java -jar perf-test.jar --queue-args x-max-length=10
上述命令会创建一个长度限制为10的队列。你也可以使用逗号分隔的键值对作为参数:
java -jar perf-test.jar --queue-args x-max-length=10,x-dead-letter-exchange=some.exchange.name
一些常用的队列参数可通过专用标记获得:
max-length-bytes:最大队列大小--leader-locator:quorum和stream队列的leader位置策略,支持client-local和balanced
支持仲裁队列
可以使用参数创建仲裁队列,但PerfTest提供了--quorum-queue标记:
java -jar perf-test.jar --quorum-queue --queue qq
--quorum-queue是--flag persistent --queue-args x-queue-type=quorum --auto-delete false的简写。注意仲裁队列不能由服务器命名,故必须用--queue参数指定队列名称。
支持流队列
PerfTest提供了标记flag和选项option以配置流队列,并基于AMQP 0.9.1使用。使用--stream-queue标记创建流而非经典队列:
java -jar perf-test.jar --stream-queue --queue sq
注意流队列不能由服务器命名,故必须用--queue参数指定队列名称。--stream-queue会自动设置--qos标记为200。
还可使用以下与流相关的标记,括号中提到的是 –stream-queue 应用的默认值(若有):
--max-length-bytes:流的最大大小,0表示无限制(20GB)--stream-max-segment-size-bytes:流报文的最大大小(500mb)--max-age:流报文的最大过期事件,使用ISO 8601时间格式,例如,PT10M30S 为 10 分 30 秒,P5DT8H 为 5 天 8 小时。
如何从流中消费数据详见流消费者部分。
NOTICE
PerfTest使用AMQP 0.9.1协议。若你想要使用流协议测试RabbitMQ Stream,请使用Stream PerTest。
自定义消息
可以自定义 PerfTest 发布的信息。这样就能尽可能接近目标流量,或在队列中填入实际用户将处理的报文。
消息属性
可以用以逗号分隔的键值对指定消息属性:
java -jar perf-test.jar --message-properties priority=5,timestamp=2007-12-03T10:15:30+01:00
支持的键值对有:contentType, contentEncoding, deliveryMode, priority, correlationId, replyTo, expiration, messageId, timestamp, type, userId, appId, clusterId,若一些给定的键值不属于前述清单,则会被作为headers(任意键/值对
任意键值对):
java -jar perf-test.jar --message-properties priority=10,header1=value1,header2=value2
从文件中获取消息体
可以通过指定消息内容和内容类型模拟真实消息。这在下游插入实际应用消费者时非常有用。内容可以来自一个或多个文件,也可以指定内容类型:
java -jar perf-test.jar --consumers 0 --body content1.json,content2.json --body-content-type application/json
随机JSON消息体
PerfTest可以为消息生成岁的JSON消息体。这对于试验(几乎)总是变化的流量非常有用。要生成随机 JSON 有效载荷,请使用 –json-body 标志和 –size 参数指定以字节为单位的大小:
java -jar perf-test.jar --json-body --size 16000
生成随机值的成本很高,因此 PerfTest 会预先生成一个消息体池,并在发布的信息中随机使用。这样,生成有效载荷就不会影响发布速度。有 2 个选项可用于更改预先生成的随机 JSON 消息体:
--body-count:PerfTest要生成的和用以发布消息的消息体池大小。大小默认为100。若发布时需要更多随机消息增加该值。--body-fiedl-count:用以生成JSON字段名和值的字符串池大小。在生成JSON消息体之前,PerfTest会生成随机字符串用于JSON文档的字段名和值。默认值为1000。增加该值有助于生成大消息体(数百KB或更多),大消息体会耗尽随机字符串池最终导致字段名重复。若使用随机JSON消息体模拟流量,重复字段名毫无问题,但如果引入真实消费者且尝试解析JSON文档就会出现问题(JSON解析器并不总是容许重复字段名)。
--fody-count和--body-field-count的默认值一般没有问题,不过可以增加其值获取更多随机性,代价是启动时间更慢,内存消耗更大。
请注意大量缓存生成的适中大小的消息体会占用相当一部分内存。例如,--json-body --body-count 50000 --size 100000(50000条100KB的消息)会占用5GB内存。
限制和改变发布速率
默认情况下,PerfTest尽可能快地发布消息。每个生产者的发布速率可以使用--rate(-r)限制。例如,每秒最多发布100条消息:
java -jar perf-test.jar --rate 100
--variable-rate(-vr)选项可以多次使用以一定一段时间范围内的发布速率。例如:
java -jar perf-test.jar --variable-rate 100:60 --variable-rate 1000:10 --variable-rate 500:15
可变速率选项使用[RATE]:[DURATION]结构,RATE单位为消息每秒,DURATION单位为秒。在上述例子中,首先会以每秒100条消息的速率持续60秒,然后以每秒1000条消息的速率持续10秒,最后以每秒500条的消息持续15秒,之后会回到每秒100条的消息持续60秒,如此循环。
选项--veriable-rate用于模拟平稳速率和短期突发消息。
设置和改变消息大小
PerfTest生产者的默认消息大小为12字节(PerfTest在消息中存储了一些数据用以在消费者侧计算延迟)。
使用--size(-s)参数可以使得消息更大,例如,发布4kb的消息:
java -jar perf-test.jar --size 4000
--variable-size(-vs)选项可以按时间指定不同消息大小,例如:
java -jar perf-test.jar --variable-size 1000:30 --variable-size 10000:20 --variable-size 5000:45
可变大小选项使用[SIZE]:[DURATION]结构,SIZE单位为字节,DURATION单位为秒。在上述例子中,首先会发送1kb大小的消息持续30秒,然后发送10kb的消息持续20秒,最后发送5kb的消息持续45秒。之后会回到1kb持续30秒,如此循环。
设置和改变消费者延迟
可以使用固定或可变的延迟(单位为微秒)模拟每条消息的处理时间。
--consumer-latency(-L)选项以微秒为单位设置一个固定消费者延迟,下面例子设置了1毫秒延迟:
java -jar perf-test.jar --consumer-latency 1000
--consumer-latency(-L)选项设置可变消费者延迟,下面例子中,先是设置1毫秒延迟持续60秒,然后是1秒延迟持续30秒:
java -jar perf-test.jar --variable-latency 1000:60 --variable-latency 1000000:30
使用多个队列
PerfTest支持消费者负载均衡和跨多个队列消费负载均衡,例如:
使用一系列队列:
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 10 \
--producers 100 --consumers 100
上述命令会创建perf-test-1、perf-test-2、…、perf_test-10队列,并为其分配生产者和消费者。以这种方式每个队列有10个生产者和10个消费者。
以循环方式负载均衡:
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 10 \
--producers 15 --consumers 30
使用上述命令,从perf-test-1到perf-test-5的队列有2个生产者,从perf-test-6到perf-test-10的队列仅有1个生产者,每个队列有3个消费者。
注意-queue-pattern值是Java print风格字符串。队列索引是仅有的传参。其风格十分接近于C的printf。--queue-pattern 'perf-test-%03d' --queue-pattern-from 1 --queue-pattern-to 500会从perf-test001到perf-test-500创建500个队列。
模拟高负载
PerfTest很轻松在一台简单台式机运行数百连接。每个生产者和消费者使用一个 Java 线程和一个 TCP 连接,因此 PerfTest 进程很快就会用完文件描述符,这取决于操作系统的设置。当与队列序列功能结合使用时,这一点尤其方便。下述命令启动了一个PerfTest进程,其创建了500个队列(从perf-test-1到perf-test-500)。每个队列有1个生产者和3个消费者。
创建第一套 500 个队列
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 500 \
--producers 500 --consumers 1500
然后下述命令启动了第二个个PerfTest进程,其创建了500个队列(从perf-test-501到perf-test-1000)。每个队列有1个生产者和3个消费者。
创建第二套500个队列
这 2 个进程将模拟分布在 1000 个队列中的 1000 个生产者和 3000 个消费者。
PerfTest进程会耗尽文件描述符限制然后抛出java.lang.OutOfMemoryError: unable to create new native thread异常。一个避免如此的办法是减少Java线程使用PerfTest使用--hearbeat-sender-threads选项:
使用--hearbeat-sender-threads以减少线程数量
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 3000 --heartbeat-sender-threads 10
默认情况,每一个生产者和消费者连接会使用一个专门的线程来给broker发送心跳,故在上述例子中会有4000个线程用于心跳。考虑到生产则会和消费者会一直通过发布消息或者发送确认与broker保持通信,连接从不会空闲,所以10个线程用于4000个连接的心跳足够了。请不要犹豫,根据自己的使用情况尝试合适的–heartbeat-sender-threads 值。
另一个避免java.lang.OutOfMemoryError: unable to create new native thread异常的方式是在操作系统层面调整文件描述符可用数量,因为有些发行版使用的限制很低。这里的建议与对broker的建议相同,因此您可以参考我们的联网指南。
具有大量客户端的工作负载
典型的联网设备工作负载(又称 “物联网工作负载”)涉及许多生产者和消费者(数十或数十万),它们以较低且基本恒定的速率交换信息,通常每几秒或几分钟交换一条信息。与吞吐量较高、客户端数量较少的工作负载相比,模拟此类工作负载需要一套不同的设置。有了适当的标志集,PerfTest 就能模拟物联网工作负载,而不需要太多资源,尤其是线程。让我们来了解一下这些标记。
在物联网工作负载中,发布者通常不会每秒发布很多信息,而是每隔固定时间发布一条信息。这可以通过使用 –publishing-interval 标记而不是 –rate 标记来实现。例如:
使用 –publishing-interval 来处理低吞吐量工作负载
java -jar perf-test.jar --publishing-interval 5
上述命令使得消费者每5秒发送一个消息。要模拟一组消费者,使用--queue-pattern标志来模拟多个队列中的多个消费者:
模拟1000个队列上的2000个客户端
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 1000 \
--heartbeat-sender-threads 10 \
--publishing-interval 5
WARNNING
注意缓慢生产者的抽象间隔--interal选项(-i)为统计设置抽样间隔且默认为1秒。在发布速度较慢的情况下(使用 –publishing-interval 时每秒发布 1 条信息或更少)保持此值,可能会导致某些指标下降,因为可能会在某段时间获取不到任何值。请注意,这只会影响指标,而不会影响 PerfTest 或代理的行为方式。为避免指标下降,可以增加抽样间隔,发布间隔的两倍是一个合理经验值,或使用 –producer-random-start-delay 选项来加快发布者的启动速度(见下文)。
为防止发布者在大致相同的时间发布信息,并更均匀地分配速率,可使用 –producer-random-start-delay 选项在第一条发布信息前添加一个随机延迟:
使用 –producer-random-start-delay 来随机传播发布信息
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 1000 \
--heartbeat-sender-threads 10 \
--publishing-interval 5 --producer-random-start-delay 120
通过上述命令,每一个生产者会以1到120秒的随机启动延迟。
使用 –publishing-interval 时,PerfTest 将使用一个线程每秒执行 100 次操作。因此,1,000 个生产者以每秒 1 条消息的速度发布消息,就会有 10 个线程忙于发布调度。使用 –producer-scheduler-threads 选项可以设置所使用的线程数。如果默认值因某些原因不合适,可以自行设置:
使用 –producer-scheduler-threads 设置发布线程数
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 1000 \
--heartbeat-sender-threads 10 \
--publishing-interval 60 --producer-random-start-delay 1800 \
--producer-scheduler-threads 5
在上述例子中,有1秒到30分钟(1800秒)启动延迟1000个生产者会每60秒发布一条消息。他们会被仅仅5个线程调度,该延迟值适合长期运行的测试。
另一个选项--consumers-thread-pools在模拟中等信息速率的众多消费者时十分有用。其允许为所有消费则会使用一个指定数量的线程池,而非默认的每个消费者一个线程池。在上述例子中,每个消费者会使用一个单线程线程池,考虑到消费者处理很快而且生产者每秒发布一个消息,这未免有些过犹不及。我们可以使用--consumers-thread-pools设置线程池数量,其将被所有消费者共享。
使用–消费者线程池来减少消费者线程的数量
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 1000 \
--heartbeat-sender-threads 10 \
--publishing-interval 60 --producer-random-start-delay 1800 \
--producer-scheduler-threads 10 \
--consumers-thread-pools 10
上述例子中为所有消费者使用了10个线程池而非默认的1000个。在这种情况下,这些都是 1 线程线程池,因此总体上只有 10 个线程,而不是 1000 个线程,这为大型物联网工作负载使用单个 PerfTest 实例模拟更多客户端节省了大量资源。
默认情况下,PerfTest 使用阻塞网络套接字 I/O 与代理进行通信。这种模式在很多情况下都能很好地适用于客户端,但 RabbitMQ Java 客户端也支持异步 I/O 模式,可以轻松调整线程等资源。这里的目标是使用尽可能少的资源,通过单个 PerfTest 实例模拟尽可能多的负载。在上面的慢速发布器示例中,少量线程就足以处理 I/O。这就是 –nio-threads 标志的作用:
使用 –nio-threads 启用 NIO 模式,减少 IO 线程数量
java -jar perf-test.jar --queue-pattern 'perf-test-%d' \
--queue-pattern-from 1 --queue-pattern-to 1000 \
--producers 1000 --consumers 1000 \
--heartbeat-sender-threads 10 \
--publishing-interval 60 --producer-random-start-delay 1800 \
--producer-scheduler-threads 10 \
--nio-threads 10
该方式PerfTest对所有连接使用12个线程进行I/O。在默认的阻塞I/O模式下,每一个生产者(或者消费者)为I/O循环使用一个线程,模拟1000个生产者和1000个消费者需要2000个线程。在PerfTest中通过适当地调整,使用NIO可以大幅减少用于模拟具有大量连接的工作负载所使用的资源。
请注意,在 NIO 模式下,当连接意外关闭、连接恢复启动时,使用的线程数可能会暂时增加。这是由于 NIO 模式会将连接关闭分派给非 I/O 线程,以避免死锁。可以使用 –disable-connection-recovery 标志禁用连接恢复。
在不同机器上运行生产者和消费者
若你在不同机器或者甚至在不同进程上运行生产者和消费者,并且想要PerfTest计算延迟,你需要使用--user-mills标志,例如,从一台机器上发送消息:
java -jar perf-test.jar --producers 1 --consumers 0 \
--predeclared --routing-key rk --queue q --use-millis
从另一台机器消费消息:
java -jar perf-test.jar --producers 0 --consumers 1 \
--predeclared --routing-key rk --queue q --use-millis
注意一旦你使用--use-millis,延迟会被PerfTest以毫秒而非微秒为单位进行计算,也要注意不同机器之间应当有时钟同步,例如NTP。若你并非在不同机器上执行生产者和消费者,或者不想PerfTest计算延迟,则不必使用--use-nills标志。
为什么需要关注--use-millis标志?PerfTest在消息中默认使用System.nanoTime()计算生产者和发送者(?)间的延迟。System.nanoTime()提供纳秒精度时间但是仅用于同一Java进程,故PerfTest反而使用System.currentTimeMillis(),其提供毫秒级精度,但只要机器间时钟同步就是可靠的。
异步消费者与同步消费者对比
PerfTest中消费者默认是异步的。这意味着消费者使用 AMQP basic.consume方法被注册,broker向消费者推送消息。这是消费消息的最佳方式。PerfTest也提供--polling和--polling-interval选项,用AMQP basic.get方法polling消息代理以消费消息。这些选项可用于评估basic.get的性能和效果,但是实际应用中应尽可能避免使用basic.get,因为与异步消费者相比有几个缺点:每个消息都需要网络往返、程序中线程忙于轮询、本质上增加了延迟。
从流中消费
RabbitMQ 流是一种具有非破坏性消费者语义的仅追加日志模型。PerfTest 使用 AMQP 0.9.1 协议与流交互。自定义队列部分介绍了如何用 PerfTest 声明流。
当从流消费时,消费者确认和预取是强制的,所以标志--qos必须指定。下面例子仅设置了消费者,从invoices流中获取数据,消费者预取为200:
java -jar perf-test.jar -x 0 -y 1 --predeclared \
--queue invoices --qos 200
注意:默认情况下,消费者被添加到流末尾(next偏移)。这意味着若那时没有生产者向流中添加消息,消费者不会获取到任何消息。使用--stream-consumer-offset标志改变默认设置,例如设置first在流的开头运行:
java -jar perf-test.jar -x 0 -y 1 --predeclared \
--queue invoices --qos 200 --stream-consumer-offset first
--stream-consumer-offset的取值有first、last、next,一个表示绝对偏移的unsigned long值,或者一个ISO 8601格式的时间戳(例如,2022-06-03T07:45:54Z)
注意
PerfTest使用AMQP 0.9.1协议,若想要使用stream协议测试RabbitMQ Stream,请使用Stream PerfTest。
同步多个实例
注意
该功能仅在Java 11以及更高时可用
PerfTest实例可以在同一时间同步启动。在使用不同工作负载且想要在同一个监控图表中监控时,这样十分有用。--id标志区分需要同步的实例组,--expected-instances标志设置组大小。
让我们举一个有点人为的例子,尽可能简化标记,并比较自动删除队列和仲裁队列的行为。我们启动第一个 PerfTest 实例:
java -jar perf-test.jar --id auto-delete-vs-qq --expected-instances 2
该实例等待,直到第二个实例准备好:
java -jar perf-test.jar --id auto-delete-vs-qq --expected-instances 2 --quorum-queue --queue qq
若想要同步通信,实例必须共享相同--id。注意,在启动同步进程之前同步实例会创建连接,当所有期望的实例加入实例组后,然后实例准备好启动他们各自的工作负载(发布者或者消费者)。
注意
实例同步与 RabbitMQ 流的性能工具 StreamPerfTest 兼容:两个工具的实例可以相互同步。这两个工具使用相同的标记来表示此功能。
默认同步超时时间为10分钟,可以使用--instance-sync-timeout标志改变,值以秒为单位。
PerfTest 实例同步要求 IP 组播(IP multicast)可用。当PerfTest运行在Kubernetes pod中时,IP组播并不必需。PerfTest 实例应在同一命名空间中运行,该命名空间必须在 MY_POD_NAMESPACE 环境变量中可用,或使用 –instance-sync-namespace 标志提供。一旦命名空间信息可用,PerfTest 就会优先列出 pod IP,而不是使用 IP 组播。下面是在 Kubernetes 上通过显式提供命名空间来使用实例同步的示例:
java -jar perf-test.jar --id workload-1 --expected-instances 2 \
--instance-sync-namespace qa
注意
PerfTest 需要获得向 Kubernetes 索取 pod IP 列表的权限。这是通过创建各种策略(如使用 YAML)来实现的。更多信息,请参阅 Kubernetes discovery protocol for JGroups 。
TLS支持
PerfTest可以使用TLS连接到一个配置接受TLS连接的节点。要使用TLS,指定一个使用amqps的URL即可:
java -jar perf-test.jar -h amqps://localhost:5671
默认情况下,PerfTest 会自动信任服务器,不会显示任何客户端证书(控制台中会显示警告)。在许多基准测试或负载测试场景中,这样就足够了。如果需要对等验证,可以在命令行中使用适当的 JVM 属性来覆盖默认的 SSLContext。例如,要信任某个服务器,可以使用:
java -Djavax.net.ssl.trustStore=/path/to/server_key.p12 \
-Djavax.net.ssl.trustStorePassword=bunnies \
-Djavax.net.ssl.trustStoreType=PKCS12 \
-jar perf-test.jar -h amqps://localhost:5671
前面的代码段定义了适当的系统属性,以查找要使用的信任存储。请参阅 TLS 指南了解如何使用 TLS 设置 RabbitMQ。为开发和 QA 环境生成 CA 和一些自签名证书/密钥对的便捷方法是使用 tls-gen。由 tls-gen 生成 TLS 文件后,你必须生成一个包含服务器或 CA 证书的信任存储文件:
keytool -import -file server_certificate.pem \
-keystore server_certificate.p12 -storepass bunnies -storetype PKCS12 \
-noprompt
下面是如何使用由 tls-gen 基本配置文件和信任存储生成的证书/密钥对运行 PerfTest:
java -Djavax.net.ssl.trustStore=/path/to/server_certificate.p12 \
-Djavax.net.ssl.trustStorePassword=bunnies \
-Djavax.net.ssl.trustStoreType=PKCS12 \
-Djavax.net.ssl.keyStore=/path/to/client_key.p12 \
-Djavax.net.ssl.keyStorePassword=bunnies \
-Djavax.net.ssl.keyStoreType=PKCS12 \
-jar perf-test.jar -h amqps://localhost:5671
OAuth2 认证/授权
可以连接到配置为使用 OAuth 2.0 Authentication Backend 的 RabbitMQ 实例。在这种情况下,无需在 AMQP URI 中提供用户名和密码:而应作为单独的命令行选项提供令牌端点 URI、客户端 ID 和客户端密钥。应一次性指定所有 3 项内容。
这是一个例子:
java -jar perf-test.jar \
--uri amqps://some-uri-without-user-and-password:5671 \
--oauth2-token-endpoint https://example.com/api/auth/token \
--oauth2-client-id 12345 \
--oauth2-client-secret qwerty \
--oauth2-grant-type client_credentials \
--oauth2-parameters orgId=1212 \
--oauth2-parameters subject_token_type=urn:ietf:params:oauth:token-type:access_token
--oauth2-grant-type时可选参数,默认为client_credential
可通过 –oauth2-parameters 选项向token endpoint传递任意数量的可选参数。
使用环境变量作为选项
环境变量有时比命令行更容易使用,例如,使用清单文件配置 PerfTest(使用 Docker Compose 或 Kubernetes),尤其是使用的选项数量增加时。
PerfTest 会自动使用与其选项长文本的蛇形版本相匹配的环境变量(例如,PerfTest会自动获取CONFIRM_TIMEOUT环境变量的值,用于–confirm-time选项,不过仅在环境变量定义时获取)。
可以使用下面命令列出PerfTest会获取的环境变量:
java -jar perf-test.jar --env
注意一些选项会被定义多个值而使用多次,例如:
java -jar perf-test.jar \
--variable-rate 100:60 --variable-rate 1000:10 --variable-rate 500:15
声明多次环境变量只会覆盖之前的值,所以要使用逗号分隔的多个值定义环境变量。
VARIABLE_RATE="100:60,1000:10,500:15"
为避免与已有的环境变量冲突,可以指定一个环境变量前缀,其会被PerfTest检查。使用环境变量RABBITMQ_PERF_TEST_ENV_PREFIX定义该前缀,例如:
RABBITMQ_PERF_TEST_ENV_PREFIX="PERF_TEST_"
例如,定义使用RABBITMQ_PERF_TEST_ENV_PREFIX="PERF_TEST_",PerfTest 将查找 PERF_TEST_CONFIRM_TIMEOUT 环境变量,而不仅仅是 CONFIRM_TIMEOUT。
HTML格式结果结果
PerfTest HTML 扩展是一套工具,可通过封装 PerfTest 帮助您运行自动基准。您可以提供基准规格,该工具将负责运行基准、收集结果并将其显示在 HTML 页面中。详见此处。
控制台输出格式
PerfTest 默认的控制台输出格式是明确的,因为每一行都包含每个值的标签:
默认输出格式
id: test-101517-299, time 1.000 s, sent: 188898 msg/s, received: 85309 msg/s, min/median/75th/95th/99th consumer latency: 24/234/364/462/474 ms
id: test-101517-299, time 2.000 s, sent: 101939 msg/s, received: 117152 msg/s, min/median/75th/95th/99th consumer latency: 483/759/830/896/907 ms
id: test-101517-299, time 3.000 s, sent: 137450 msg/s, received: 118324 msg/s, min/median/75th/95th/99th consumer latency: 691/816/854/893/909 ms
高级用户如果喜欢更紧凑的格式,可以使用 –metrics-format compact 选项(简写 -mf compact)。输出结果如下
compact输出格式
time sent received consumer latency
1.000s 173920 msg/s 84405 msg/s 1/25/189/312/331 ms
2.000s 133044 msg/s 117703 msg/s 329/728/814/887/897 ms
3.000s 103736 msg/s 117134 msg/s 705/804/846/892/920 ms
监控
PerfTest 可以收集指标,并将其提供给各种监控系统。指标包括以消息传递为中心的指标(消息延迟、连接数和通道数、发布消息数等)以及操作系统进程和 JVM 指标(内存、CPU 使用率、垃圾回收、JVM 堆等)。
下面是列出可用指标的选项:
java -jar perf-test.jar --metrics-help
该命令显示启用 PerfTest 可以收集的各种指标的可用标记,以及配置 PerfTest 支持的监控系统的暴露选项。
支持的指标
下面是PerfTest可以收集的指标:
- 默认指标:发布、return、确认、nack、消费的消息数,消息延迟,发布者确认延迟。在许多类型的工作负载中,信息延迟都是一个主要问题,在这里可以很容易地对其进行监控。发布者确认延迟反映了信息被认为不安全的时间。一旦使用–confirm/-c 选项,它就会被计算出来。只要启用了 PerfTest 对监控系统的支持,就可以使用默认指标。
- 客户端指标:Java客户端指标。与默认的 PerfTest 指标相比,启用这些指标不会带来太多好处,只是可以看到 PerfTest 在打开连接数和通道数等方面的表现。客户端指标使用 -mc 或 –metrics-client 标志启用。
- JVM 内存指标:这些指标报告 JVM 的内存使用情况,如当前堆大小等。这些指标有助于更好地了解客户端行为,例如,堆内存波动可能是由于频繁的垃圾回收造成的,这可以解释高延迟数字。使用 -mjm 或 –metrics-jvm-memory 标记可启用这些指标。
- JVM 线程指标:这些指标报告 PerfTest 进程中使用的 JVM 线程数量及其状态。这有助于优化 PerfTest 的使用,以较少的资源模拟高负载。使用 -mjt 或 –metrics-jvm-thread 标志可启用这些指标。
- JVM GC 指标:这些指标报告垃圾回收活动。它们会因使用的 JVM、其版本和 GC 设置而不同。它们有助于将 GC 活动与 PerfTest 行为相关联,例如,由于垃圾收集非常频繁而导致吞吐量异常低。使用 -mjgc 或 –metrics-jvm-gc 标志可启用这些指标。
- JVM 类加载器指标:已加载和未加载类的数量。使用 -mcl 或 –metrics-class-loader 标记可启用这些指标。
- 处理器指标:这些指标报告 JVM 收集到的 CPU 活动。可以使用 -mjp 或 –metrics-processor 标志启用它们。
标签
可以使用 -mt 或 –metrics-tags 选项指定指标标签,例如,使用 –metrics-tags env=performance,datacenter=eu 来告诉监控系统这些指标来自位于 eu 数据中心的performance环境。这样,支持维度的监控系统就能更轻松地浏览各种指标(分组、向下钻取)。有关标签和维度的更多信息,请参阅 Micrometer 文档。
支持的监控系统
PerfTest 以 Micrometer 为基础,向各种监控系统报告收集到的指标。不过,并非所有 Micrometer 支持的系统都能得到 PerfTest 的实际支持。PerfTest 目前支持 Datadog、JMX 和 Prometheus。请随时申请对其他监控系统的支持。
Datadog
API 密钥是向 Datadog 发送指标的唯一必需选项:
java -jar perf-test.jar --metrics-datadog-api-key YOUR_API_KEY
另一个有用的选项是步长或报告频率。默认值为 10 秒。
java -jar perf-test.jar --metrics-datadog-api-key YOUR_API_KEY \
--metrics-datadog-step-size 20
JMX
JMX 支持提供了一种在本地查看指标的简单方法。使用 –metrics-jmx 标志可将指标导出到 JMX:
java -jar perf-test.jar --metrics-jmx
Prometheus
使用 -mpr 或 –metrics-prometheus 标志启用向 Prometheus 报告指标的功能:
java -jar perf-test.jar --metrics-prometheus
Prometheus 希望对单个应用程序实例的指标进行scrape或者poll,因此 PerfTest 会启动一个监听端口为 8080 的网络服务器,并在 /metrics 端点上公开指标。这些默认值可以更改:
java -jar perf-test.jar --metrics-prometheus \
--metrics-prometheus-port 8090 --metrics-prometheus-endpoint perf-test-metrics
PerfTest 会自动公开 2 个 expected_published 和 expected_consumed 指标,分别代表理论上的发布率和消耗率。PertTest 会计算这些值,并在提供速率指令(如使用 –rate 或 –consumer-rate 时)后立即公开这些指标。
这些预期指标旨在帮助外部监控工具在实际比率与预期比率不同时触发警报。PerfTest 会尽最大努力计算和更新预期速率,但它可能会出错,或者只是无法计算出正确的值。这时可以使用 –exposed-metrics 选项(简称 -em)来覆盖指标值:
java -jar perf-test.jar --metrics-prometheus \
--exposed-metrics expected_published=50000,expected_consumed=50000
注意 PerfTest 会自动为所提供的名称添加指标前缀(默认为 perftest_)。
还可以公开任何指标,例如为发布者确认延迟设置一个预期值,这样如果实际延迟更高,外部监控系统就会触发警报:
java -jar perf-test.jar --metrics-prometheus --rate 1000 \
--exposed-metrics expected_confirm_latency=0.1