AMQP 1.0 基准测试
这篇博客文章表明,RabbitMQ 4.0 中的 原生 AMQP 1.0 与 RabbitMQ 3.13 中的 AMQP 1.0 相比,在性能和可伸缩性方面有了显著的提升。
此外,这篇博客文章建议,在 RabbitMQ 4.0 中,AMQP 1.0 的性能可能略优于 AMQP 0.9.1。
设置
以下设置适用于本博文中的所有基准测试
- Intel NUC 11
- 8 个 CPU 核心
- 32 GB 内存
- Ubuntu 22.04
- 单节点 RabbitMQ 服务器
- 服务器运行时(仅)使用 3 个调度线程(通过 运行时标志 设置为
+S 3) - Erlang/OTP 27.0.1
- 客户端和服务器运行在同一台设备上
我们在撰写本文时使用了最新的 RabbitMQ 版本
应用了以下 advanced.config 配置
[
{rabbit, [
{loopback_users, []}
]},
{rabbitmq_management_agent, [
{disable_metrics_collector, true}
]}
].
rabbitmq_management_agent 插件中的指标收集功能已禁用。对于 生产环境,推荐使用 Prometheus。
RabbitMQ 服务器启动方式如下
make run-broker \
TEST_TMPDIR="$HOME/scratch/rabbit/test" \
RABBITMQ_CONFIG_FILE="$HOME/scratch/rabbit/advanced.config" \
PLUGINS="rabbitmq_prometheus rabbitmq_management rabbitmq_amqp1_0" \
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+S 3"
rabbitmq_amqp1_0 插件在 RabbitMQ 4.0 中是一个 无操作(no-op)插件。
AMQP 1.0 基准测试在 Docker 容器中运行 quiver
$ docker run -it --rm --add-host host.docker.internal:host-gateway ssorj/quiver:latest
bash-5.1# quiver --version
quiver 0.4.0-SNAPSHOT
经典队列
本节对 经典队列 (classic queues) 进行基准测试。
我们声明了一个名为 my-classic-queue 的经典队列
deps/rabbitmq_management/bin/rabbitmqadmin declare queue \
name=my-classic-queue queue_type=classic durable=true
RabbitMQ 4.0 中的 AMQP 1.0
客户端发送并接收 100 万条消息。每条消息包含 12 字节的负载。接收方每次重复补充 200 个 链路信用 (link credits)。
# quiver //host.docker.internal//queues/my-classic-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 200
RESULTS
Count ............................................. 1,000,000 messages
Duration ............................................... 10.1 seconds
Sender rate .......................................... 99,413 messages/s
Receiver rate ........................................ 99,423 messages/s
End-to-end rate ...................................... 99,413 messages/s
Latencies by percentile:
0% ........ 0 ms 90.00% ........ 1 ms
25% ........ 1 ms 99.00% ........ 2 ms
50% ........ 1 ms 99.90% ........ 2 ms
100% ........ 9 ms 99.99% ........ 9 ms
RabbitMQ 3.13 中的 AMQP 1.0
# quiver //host.docker.internal//amq/queue/my-classic-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 200
RESULTS
Count ............................................. 1,000,000 messages
Duration ............................................... 45.9 seconds
Sender rate .......................................... 43,264 messages/s
Receiver rate ........................................ 21,822 messages/s
End-to-end rate ...................................... 21,790 messages/s
Latencies by percentile:
0% ....... 67 ms 90.00% .... 24445 ms
25% .... 23056 ms 99.00% .... 24780 ms
50% .... 23433 ms 99.90% .... 24869 ms
100% .... 24873 ms 99.99% .... 24873 ms
相同的基准测试在 RabbitMQ 3.13 上运行,吞吐量低了 4.5 倍。
详细测试执行过程
---------------------- Sender ----------------------- --------------------- Receiver ---------------------- --------
Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Lat [ms]
----------------------------------------------------- ----------------------------------------------------- --------
2.1 130,814 65,342 8 79.1 2.1 3,509 1,753 1 7.5 777
4.1 206,588 37,849 6 79.1 4.1 5,995 1,242 0 7.5 2,458
6.1 294,650 43,987 6 79.1 6.1 9,505 1,753 1 7.5 5,066
8.1 360,184 32,734 5 79.4 8.1 13,893 2,194 0 7.5 6,190
10.1 458,486 49,102 6 79.4 10.1 15,793 950 1 7.5 9,259
12.1 524,020 32,734 5 79.4 12.1 21,644 2,923 1 7.5 11,163
14.1 622,322 49,102 5 79.4 14.1 25,154 1,753 1 7.5 13,451
16.1 687,856 32,734 4 79.4 16.1 27,639 1,241 1 7.5 15,246
18.1 786,158 49,102 6 81.0 18.1 30,124 1,241 1 7.5 17,649
20.1 884,460 49,102 6 81.0 20.1 32,610 1,242 1 7.5 19,408
22.1 949,994 32,734 4 81.0 22.1 35,535 1,462 0 7.5 21,293
24.1 999,912 24,934 4 81.8 24.1 38,167 1,315 1 7.5 23,321
26.1 999,974 31 2 0.0 26.1 117,745 39,749 11 7.5 24,475
- - - - - 28.1 202,589 42,380 11 7.5 24,364
- - - - - 30.1 292,554 44,938 13 7.5 24,244
- - - - - 32.1 377,691 42,526 15 7.5 23,955
- - - - - 34.1 469,704 45,961 14 7.5 23,660
- - - - - 36.1 555,719 42,965 12 7.5 23,463
- - - - - 38.1 649,048 46,618 12 7.5 23,264
- - - - - 40.1 737,696 44,280 15 7.5 23,140
- - - - - 42.1 826,491 44,353 15 7.5 23,100
- - - - - 44.1 917,187 45,303 16 7.5 23,066
- - - - - 46.1 999,974 41,394 14 0.0 22,781
RabbitMQ 4.0 中的 AMQP 0.9.1
对于 AMQP 0.9.1 基准测试,我们使用 PerfTest。我们试图对之前的 AMQP 1.0 基准测试进行公平的对比。
由于 AMQP 1.0 的 /queues/:queue 目标地址会发送到默认交换机,我们同样通过 AMQP 0.9.1 发送到默认交换机。由于我们在 AMQP 1.0 中使用了 持久化 (durable) 消息,因此在 AMQP 0.9.1 中设置了 persistent 标志。由于 RabbitMQ 在消息无法路由时会以 已释放 (released) 状态结算,我们在 AMQP 0.9.1 中设置了 mandatory 标志。由于 RabbitMQ v4.0.0-beta.5 使用默认的 rabbit.max_link_credit 为 128,当剩余信用低于 0.5 * 128 时,会向发送客户端再授予 128 个信用,因此我们将 AMQP 0.9.1 发布者配置为每次最多有 1.5 * 128 = 192 条未确认消息。由于我们在之前的运行中使用了 200 个链路信用,我们为 AMQP 0.9.1 消费者配置了 200 的 预取 (prefetch)。
$ java -jar target/perf-test.jar \
--predeclared --exchange amq.default \
--routing-key my-classic-queue --queue my-classic-queue \
--flag persistent --flag mandatory \
--pmessages 1000000 --size 12 --confirm 192 --qos 200 --multi-ack-every 200
id: test-151706-485, sending rate avg: 88534 msg/s
id: test-151706-485, receiving rate avg: 88534 msg/s
id: test-151706-485, consumer latency min/median/75th/95th/99th 99/975/1320/1900/2799 µs
id: test-151706-485, confirm latency min/median/75th/95th/99th 193/1691/2113/2887/3358 µs
总结
仲裁队列
本节对 仲裁队列 (quorum queues) 进行基准测试。
我们声明了一个名为 my-quorum-queue 的仲裁队列
deps/rabbitmq_management/bin/rabbitmqadmin declare queue \
name=my-quorum-queue queue_type=quorum durable=true
流量控制配置
为了实现最高的数据安全性,仲裁队列会 fsync 所有 Ra 命令,包括
- 入队 (enqueue):发送者将消息入队
- 结算 (settle):接收者接收消息
- 信用 (credit):接收者补充链路信用
在仲裁队列向发布者确认收到消息之前,它会确保所有文件修改都已刷入磁盘,从而即使 RabbitMQ 节点在之后立即崩溃,数据也是安全的。
我 Linux 设备上的 SSD 速度较慢,每次 fsync 需要 5-15 毫秒。由于我们希望在不被廉价磁盘成为瓶颈的情况下比较 AMQP 协议实现,本节中的测试增加了流量控制设置
advanced.config
[
{rabbit, [
{loopback_users, []},
%% RabbitMQ internal flow control for AMQP 0.9.1
%% Default: {400, 200}
{credit_flow_default_credit, {5000, 2500}},
%% Maximum incoming-window of AMQP 1.0 session.
%% Default: 400
{max_incoming_window, 5000},
%% Maximum link-credit RabbitMQ grants to AMQP 1.0 sender.
%% Default: 128
{max_link_credit, 2000},
%% Maximum link-credit RabbitMQ AMQP 1.0 session grants to sending queue.
%% Default: 256
{max_queue_credit, 5000}
]},
{rabbitmq_management_agent, [
{disable_metrics_collector, true}
]}
].
此配置允许在 RabbitMQ 调用 fsync 之前批处理更多的 Ra 命令。对于生产环境,我们建议使用 fsync 更快的企业级高性能磁盘,在这种情况下,通常不需要增加流量控制设置。
RabbitMQ 流量控制设置是一种权衡
- 低值可确保生产环境中的稳定性。
- 高值可以提高单个连接的性能,但在大量连接并发发布大消息时,可能会导致更高的内存峰值。
RabbitMQ 使用保守的流量控制默认设置,旨在优先考虑生产环境的稳定性而非性能测试结果。
RabbitMQ 4.0 中的 AMQP 1.0
# quiver //host.docker.internal//queues/my-quorum-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
RESULTS
Count ............................................. 1,000,000 messages
Duration ............................................... 12.0 seconds
Sender rate .......................................... 83,459 messages/s
Receiver rate ........................................ 83,396 messages/s
End-to-end rate ...................................... 83,181 messages/s
Latencies by percentile:
0% ........ 9 ms 90.00% ....... 47 ms
25% ....... 27 ms 99.00% ....... 61 ms
50% ....... 35 ms 99.90% ....... 76 ms
100% ....... 81 ms 99.99% ....... 81 ms
默认流量控制设置
之前的基准测试在 ra_log_wal 模块(实现了 Raft 预写日志)中调用了 1,244 次 fsync。
在默认流量控制设置下进行相同的基准测试,调用了 15,493 次 fsync,导致吞吐量显著降低
# quiver //host.docker.internal//queues/my-quorum-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
RESULTS
Count ............................................. 1,000,000 messages
Duration .............................................. 100.2 seconds
Sender rate ........................................... 9,986 messages/s
Receiver rate ......................................... 9,987 messages/s
End-to-end rate ....................................... 9,983 messages/s
Latencies by percentile:
0% ....... 10 ms 90.00% ....... 24 ms
25% ....... 14 ms 99.00% ....... 30 ms
50% ....... 18 ms 99.90% ....... 38 ms
100% ....... 55 ms 99.99% ....... 47 ms
每次 fsync 平均耗时 5.9 毫秒。
(15,493 - 1,244) * 5.9 ms = 84 seconds
因此,相比之前增加流量控制设置的基准测试,该使用默认流量控制设置的测试在执行 fsync 上被阻塞了多 84 秒。这表明企业级高性能磁盘对于获得仲裁队列的最佳结果至关重要。对于生产工作负载,我们建议使用 fsync 延迟更低的磁盘,而不是调整 RabbitMQ 流量控制设置。
值得注意的是,Raft WAL 日志由给定 RabbitMQ 节点上的所有仲裁队列副本共享。这意味着当有数十个仲裁队列和数百个连接时,ra_log_wal 会自动将多个 Raft 命令(操作)批处理到单个 fsync 调用中。因此,当节点流量较大时,平均而言,将单个 Ra 命令刷新到磁盘的成本会降低。我们的基准测试在某种程度上人为地以单连接尽可能快的速度进行了测试。
RabbitMQ 3.13 中的 AMQP 1.0
# quiver //host.docker.internal//amq/queue/my-quorum-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
---------------------- Sender ----------------------- --------------------- Receiver ---------------------- --------
Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Lat [ms]
----------------------------------------------------- ----------------------------------------------------- --------
2.1 163,582 81,709 11 84.2 2.1 29,548 14,759 3 7.5 840
4.1 336,380 86,356 12 185.3 4.1 29,840 146 0 7.5 2,331
6.1 524,026 93,729 14 328.0 6.1 29,840 0 0 7.5 0
8.1 687,864 81,837 11 462.3 8.1 31,302 730 1 7.5 6,780
10.1 884,470 98,303 14 605.4 10.1 31,447 72 0 7.5 7,897
12.1 999,924 57,669 7 687.5 12.1 31,447 0 0 7.5 0
14.1 999,924 0 0 687.5 14.1 31,447 0 0 7.5 0
16.1 999,924 0 0 687.5 16.1 31,447 0 1 7.5 0
18.1 999,924 0 1 688.3 18.1 31,447 0 0 7.5 0
receiver timed out
20.1 999,924 0 0 688.3 20.1 31,447 0 0 7.5 0
RabbitMQ 3.13 无法处理此工作负载,基准测试失败。
默认流量控制设置
在默认流量控制设置下,该基准测试同样失败。
# quiver //host.docker.internal//amq/queue/my-quorum-queue \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
---------------------- Sender ----------------------- --------------------- Receiver ---------------------- --------
Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Lat [ms]
----------------------------------------------------- ----------------------------------------------------- --------
2.1 130,814 65,342 9 70.0 2.1 26,915 13,437 6 7.5 1,213
4.1 196,348 32,718 5 70.2 4.1 28,084 584 0 7.5 3,093
6.1 261,882 32,734 7 70.2 6.1 30,131 1,022 1 7.5 4,952
8.1 360,184 49,126 6 70.2 8.1 32,325 1,096 0 7.5 6,637
10.1 425,718 32,734 6 70.2 10.1 34,225 949 1 7.5 8,089
12.1 491,252 32,734 5 70.2 12.1 34,225 0 0 7.5 0
14.1 589,554 49,102 7 70.2 14.1 34,225 0 0 7.5 0
16.1 655,088 32,734 5 70.2 16.1 34,225 0 0 7.5 0
18.1 720,622 32,734 6 70.2 18.1 34,225 0 0 7.5 0
receiver timed out
RabbitMQ 4.0 中的 AMQP 0.9.1
由于我们将 max_link_credit 设置为 2,000,因此允许发布者中最多有 2,000 * 1.5 = 3,000 条未确认消息。
$ java -jar target/perf-test.jar \
--predeclared --exchange amq.default \
--routing-key my-quorum-queue --queue my-quorum-queue \
--flag persistent --flag mandatory \
--pmessages 1000000 --size 12 --confirm 3000 --qos 5000 --multi-ack-every 5000
id: test-085526-136, sending rate avg: 70067 msg/s
id: test-085526-136, receiving rate avg: 70067 msg/s
id: test-085526-136, consumer latency min/median/75th/95th/99th 8803/33127/40424/53407/62883 µs
id: test-085526-136, confirm latency min/median/75th/95th/99th 8551/30323/38317/52103/63131 µs
默认流量控制设置
$ java -jar target/perf-test.jar \
--predeclared --exchange amq.default \
--routing-key my-quorum-queue --queue my-quorum-queue \
--flag persistent --flag mandatory \
--pmessages 1000000 --size 12 --confirm 192 --qos 5000 --multi-ack-every 5000
id: test-084359-441, sending rate avg: 9931 msg/s
id: test-084359-441, receiving rate avg: 9931 msg/s
id: test-084359-441, consumer latency min/median/75th/95th/99th 7512/17054/26256/34249/38641 µs
id: test-084359-441, confirm latency min/median/75th/95th/99th 9432/16586/23918/32636/36858 µs
这些结果与 RabbitMQ 4.0 中 AMQP 1.0 的默认流量控制设置的结果相似,因为这两个基准测试都受到了我慢速磁盘的瓶颈限制。
总结
流
本节对 流 (streams) 进行基准测试。
我们声明了一个名为 my-stream 的流
deps/rabbitmq_management/bin/rabbitmqadmin declare queue \
name=my-stream queue_type=stream durable=true
(我们使用 RabbitMQ 默认的流量控制设置。)
我们希望接收方从流的开头开始消费。Quiver 不支持向 源 (source) 传递 filter 字段,从而无法指定 rabbitmq:stream-offset-spec 的值为 first。因此,为了方便进行此基准测试,我们修改了 RabbitMQ,使其默认使用流偏移量规范 first 而非 next。
git diff
diff --git a/deps/rabbit/src/rabbit_stream_queue.erl b/deps/rabbit/src/rabbit_stream_queue.erl
index e36ad708eb..acd193d76f 100644
--- a/deps/rabbit/src/rabbit_stream_queue.erl
+++ b/deps/rabbit/src/rabbit_stream_queue.erl
@@ -344,7 +344,7 @@ consume(Q, Spec, #stream_client{} = QState0)
{term(), non_neg_integer()}) ->
{ok, osiris:offset_spec()} | {error, term()}.
parse_offset_arg(undefined) ->
- {ok, next};
+ {ok, first};
parse_offset_arg({_, <<"first">>}) ->
{ok, first};
parse_offset_arg({_, <<"last">>}) ->
RabbitMQ 4.0 中的 AMQP 1.0
# quiver //host.docker.internal//queues/my-stream \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
---------------------- Sender ----------------------- --------------------- Receiver ---------------------- --------
Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Lat [ms]
----------------------------------------------------- ----------------------------------------------------- --------
2.1 278,782 139,321 25 8.0 2.1 215,185 107,539 22 7.6 224
4.1 554,492 137,717 25 8.0 4.1 434,027 109,312 24 7.6 651
6.1 825,082 135,160 25 8.0 6.1 650,236 107,997 26 7.6 1,079
8.1 999,992 87,368 17 0.0 8.1 888,973 119,249 29 7.6 1,469
- - - - - 10.1 999,993 55,455 13 0.0 1,583
RESULTS
Count ............................................. 1,000,000 messages
Duration ................................................ 8.9 seconds
Sender rate ......................................... 136,705 messages/s
Receiver rate ....................................... 112,587 messages/s
End-to-end rate ..................................... 112,196 messages/s
Latencies by percentile:
0% ........ 7 ms 90.00% ..... 1553 ms
25% ...... 519 ms 99.00% ..... 1612 ms
50% ..... 1011 ms 99.90% ..... 1615 ms
100% ..... 1616 ms 99.99% ..... 1616 ms
可以轻易观察到明显更高的吞吐量。
注意,端到端延迟非常高,仅仅是因为发送者写入流的速率高于 RabbitMQ 将消息分发给消费者(quiver 术语中的“接收者”)的速率。
RabbitMQ 3.13 中的 AMQP 1.0
# quiver //host.docker.internal//amq/queue/my-stream \
--durable --count 1m --duration 10m --body-size 12 --credit 5000
---------------------- Sender ----------------------- --------------------- Receiver ---------------------- --------
Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Time [s] Count [m] Rate [m/s] CPU [%] RSS [M] Lat [ms]
----------------------------------------------------- ----------------------------------------------------- --------
2.1 196,350 98,077 12 70.1 2.1 4,094 2,045 0 7.7 195
4.1 392,956 98,205 13 138.5 4.1 4,094 0 0 7.7 0
6.1 524,026 65,470 10 196.5 6.1 4,094 0 0 7.7 0
8.1 655,096 65,470 11 259.4 8.1 4,094 0 0 7.7 0
10.1 786,166 65,470 10 307.5 10.1 4,094 0 0 7.7 0
receiver timed out
12.1 917,236 65,470 9 355.5 12.1 4,094 0 0 7.7 0
RabbitMQ 3.13 无法处理此工作负载,基准测试失败。
RabbitMQ 4.0 中的 AMQP 0.9.1
$ java -jar target/perf-test.jar \
--predeclared --exchange amq.default \
--routing-key my-stream --queue my-stream \
--flag persistent --flag mandatory \
--pmessages 1000000 --size 12 --confirm 192 --qos 5000 --multi-ack-every 5000
id: test-104223-225, sending rate avg: 88912 msg/s
id: test-104223-225, receiving rate avg: 88912 msg/s
id: test-104223-225, consumer latency min/median/75th/95th/99th 701/1340/1523/2500/4524 µs
id: test-104223-225, confirm latency min/median/75th/95th/99th 788/1983/2130/2437/2970 µs
由于流以 AMQP 1.0 格式存储消息,因此该工作负载需要 RabbitMQ 在 AMQP 0.9.1 和 AMQP 1.0 之间转换每条消息。这解释了为什么在使用 AMQP 0.9.1 客户端时,流的吞吐量比使用 AMQP 1.0 客户端时要低。
总结
大量连接
本节比较了连接 40,000 个客户端(每个连接两个 AMQP 1.0 会话 / AMQP 0.9.1 通道)的内存使用情况。
设置
make run-broker \
TEST_TMPDIR="$HOME/scratch/rabbit/test" \
RABBITMQ_CONFIG_FILE="$HOME/scratch/rabbit/rabbitmq.conf" \
PLUGINS="rabbitmq_amqp1_0" \
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+P 3000000 +S 6" \
ERL_MAX_PORTS=3000000
在下方的 rabbitmq.conf 中,我们使用较小的缓冲区大小,以便更好地比较协议实现的内存使用情况。
tcp_listen_options.sndbuf = 2048
tcp_listen_options.recbuf = 2048
vm_memory_high_watermark.relative = 0.95
vm_memory_high_watermark_paging_ratio = 0.95
loopback_users = none
AMQP 1.0
package main
import (
"context"
"log"
"time"
"github.com/Azure/go-amqp"
)
func main() {
for i := 0; i < 40_000; i++ {
if i%1000 == 0 {
log.Printf("opened %d connections", i)
}
conn, err := amqp.Dial(
context.TODO(),
"amqp://",
&amqp.ConnOptions{SASLType: amqp.SASLTypeAnonymous()})
if err != nil {
log.Fatal("open connection:", err)
}
_, err = conn.NewSession(context.TODO(), nil)
if err != nil {
log.Fatal("begin session:", err)
}
_, err = conn.NewSession(context.TODO(), nil)
if err != nil {
log.Fatal("begin session:", err)
}
}
log.Println("opened all connections")
time.Sleep(5 * time.Hour)
}
AMQP 0.9.1
package main
import (
"log"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
func main() {
for i := 0; i < 40_000; i++ {
if i%1000 == 0 {
log.Printf("opened %d connections", i)
}
conn, err := amqp.Dial("amqp://guest:guest@localhost")
if err != nil {
log.Fatal("open connection:", err)
}
_, err = conn.Channel()
if err != nil {
log.Fatal("open channel:", err)
}
_, err = conn.Channel()
if err != nil {
log.Fatal("open channel:", err)
}
}
log.Println("opened all connections")
time.Sleep(5 * time.Hour)
}
下方的示例直接在节点上调用 erlang:memory/0,该函数返回每种内存类型的字节大小。
rabbitmq-diagnostics
要从正在运行的节点获取相同信息,请使用 CLI 命令 rabbitmq-diagnostics,如下所示
rabbitmq-diagnostics memory_breakdown
此命令可以使用不同的信息单位(如 MiB、GiB)格式化数字,并支持通过 --formatter=json 输出 JSON。
# pipes the output to `jq` for more readable formatting
rabbitmq-diagnostics memory_breakdown --formatter=json | jq
RabbitMQ 4.0 中的 AMQP 1.0
以下是运行时报告的内存占用数值
1> erlang:memory().
[{total,5330809208},
{processes,4788022888},
{processes_used,4787945960},
{system,542786320},
{atom,999681},
{atom_used,974364},
{binary,194810368},
{code,19328950},
{ets,94161808}]
2> erlang:system_info(process_count).
360312
RabbitMQ 3.13 中的 AMQP 1.0
作为对比,此测试中运行时报告的内存占用数值为
1> erlang:memory().
[{total,12066294144},
{processes,11156497904},
{processes_used,11156461208},
{system,909796240},
{atom,1089809},
{atom_used,1062780},
{binary,192784464},
{code,22068126},
{ets,318872128}]
2> erlang:system_info(process_count).
1480318
我们观察到 RabbitMQ 3.13 中 processes 的内存使用量为 11.1 GB,而 RabbitMQ 4.0 中仅为 4.8 GB(减少了约 56%)。正如 之前 的博文所解释,RabbitMQ 3.13 中的 AMQP 1.0 实现资源占用较高,因为该插件中的每个 AMQP 1.0 会话都包含一个 AMQP 0.9.1 客户端并维护 AMQP 0.9.1 状态。
RabbitMQ 4.0 中的 AMQP 0.9.1
1> erlang:memory().
[{total,5409763512},
{processes,4716150248},
{processes_used,4715945080},
{system,693613264},
{atom,991489},
{atom_used,962578},
{binary,187229040},
{code,19118766},
{ets,235605424}]
2> erlang:system_info(process_count).
600314
总结
结论
本博文展示了 RabbitMQ 4.0 中新的原生 AMQP 1.0 实现的性能比 RabbitMQ 3.13 中的 AMQP 1.0 高出数倍。
我们也观察到 AMQP 1.0 的性能可能优于 AMQP 0.9.1。然而,提供公平的对比是很有挑战性的。本博文使用了 C 编写的 AMQP 1.0 客户端和 Java 编写的 AMQP 0.9.1 客户端。因此,我们并不声称或承诺您的 AMQP 1.0 工作负载一定会获得更高的吞吐量。RabbitMQ 中的 AMQP 0.9.1 实现性能良好,因为它已经稳定且优化了超过 15 年。
AMQP 1.0 可能优于 AMQP 0.9.1 的场景包括:
- 发送或接收流,因为流以 AMQP 1.0 格式对消息进行编码(如本博文所述)。
- 使用 RabbitMQ AMQP 1.0 Java 客户端 利用队列局部性。(此功能将另行介绍。)
- 当同一连接上的某个目标队列达到其限制时,发布到其他队列或从中进行消费(如 AMQP 1.0 流量控制 博文所述)。
