压力测试

在两套环境上对代理服务器施加负载——一套经网络,一套走回环——以便把代理服务器本身的表现和环境的影响区分开。下面是方法、完整表格和简短结论。

下载报告(PDF)

测试环境

日期:2026 年 7 月 24 日 · 协议:MQTT 3.1.1 · 工具:自研负载客户端 cmd/loadtest,基于本项目自己的编解码器,未使用第三方 MQTT 库。

为把代理服务器本身的表现与环境的影响区分开,测试在两套环境上进行:经网络与走回环,普通机器与高性能机器。

环境 A —— 经网络环境 B —— 回环
代理服务器 CPU12 核2× Xeon E5-2690 v4 @ 2.6 GHz(28 核 / 56 线程)
代理服务器内存16 GB128 GB
地址192.168.1.84:1883(局域网)127.0.0.1:1883(回环)
负载生成端局域网上另一台机器与代理服务器同一台机器
客户端临时端口16,384(默认)55,000(已放宽)
如何看待这份对比。两套环境并不等价,回答的问题也不同。环境 A 展示的是经由网络、并有专用生成机时的表现。环境 B 去掉了网络(时延几乎为零)并给代理服务器配了强力硬件,但生成端与代理服务器共用同一批核心。因此时延和扇出可以正面比较,而接收速率不能:在环境 B 上,客户端是在跟代理服务器抢核心。

结论概要

  • 时延。常规负载下「发布者 → 代理服务器 → 订阅者」的端到端时延,经网络约为 1 毫秒;走回环为 73 µs(去掉网络后低约 16 倍)。
  • 连接数。经网络时代理服务器稳住了 10,000 条会话且零失败(再往上就撞到客户端的端口上限)。在强力硬件上走回环:30,000 条零失败,冲击 50,000 时留下 约 41,000(这是单台机器的极限,而不是代理服务器的)。
  • 接收量。经网络的峰值约为 每秒 277,000 条消息;走回环时较低(每秒约 185,000),因为生成端在与代理服务器争抢核心。
  • 扇出(分发)。在强力硬件上,代理服务器向订阅者投递了 每秒约 1,350,000 条消息(扇出 ×100),而经网络时约为每秒 22,000 条。
  • 稳健性。两套环境下代理服务器都没有崩溃也没有卡死。过载时,发往落后订阅者的 QoS 0 消息会被有序丢弃;所有测试中代理服务器的日志里没有一条错误。

并发连接容量

每条连接都是一次带认证的完整 CONNECT → CONNACK 流程,随后保持会话打开(用 PINGREQ 核实)。

环境 A —— 经网络

目标已建立失败建立 p50p99
10100146 ms162 ms
1001000702 ms1.12 s
1,0001,00004.21 s9.78 s
5,0005,00004.64 s8.59 s
10,00010,00004.92 s10.52 s
15,00014,8791217.43 s14.09 s

环境 B —— 回环(逐步加压)

目标已建立失败建立 p50p99
1,0001,0000886 ms1.06 s
10,00010,0000903 ms1.55 s
30,00030,0000923 ms1.90 s
50,00041,2190984 ms1.90 s

分析

  • 经网络时到 10,000 为止零失败;15,000 时的 121 次失败,是客户端临时端口(16,384)耗尽所致。那是生成端的极限,不是代理服务器的。
  • 走回环时,把端口范围放宽之后,代理服务器完好地维持了 30,000 条会话;冲到 50,000 时,保持期结束时仍有约 41,000 条存活——毕竟两端共用一台机器。整个过程中代理服务器的日志始终干净,没有错误。
  • 附带发现。走回环时,连接建立得太猛(同时发起 1,000 次 dial)会触发 connection refused:接受队列被挤爆了,因为没有网络时延,所有 SYN 会一齐涌到。逐步加压(每次 200 条)能完全消除这一现象。经由网络时,时延本身就把这个尖峰抹平了。

接收吞吐

一组发布者以尽可能快的速度发布,测量代理服务器的接收速率。

QoS 0 · 64 字节载荷

发布者环境 A(网络)环境 B(回环)
10271,006/秒185,012/秒
100236,422/秒101,457/秒
500103,546/秒125,324/秒

经网络时接收量更高,并不是因为环境 A 的代理服务器更快,而是因为那里的生成端跑在另一台机器上。走回环时客户端与代理服务器共用同一批核心,因此总速率被压住了。

QoS 1(带 PUBACK 确认)· 64 B

发布者环境 A(网络)环境 B(回环)
1010,292/秒63,606/秒
10022,085/秒48,983/秒

QoS 1 的测试客户端是同步的(每发一条就等一次 PUBACK),因此受制于往返时间。走回环时往返几乎为零,所以快了约 6 倍。这清楚地说明,QoS 1 的数字是由网络时延决定的,而不是代理服务器。

不同消息长度 · QoS 0 · 50 个发布者

长度环境 A(网络)环境 B(回环)
64 B277,198/秒 · 17.7 MB/秒101,961/秒 · 6.5 MB/秒
256 B124,997/秒 · 32.0 MB/秒102,029/秒 · 26.1 MB/秒
1,024 B38,458/秒 · 39.4 MB/秒102,151/秒 · 104.6 MB/秒

两个不同的天花板:

  • 经网络时在每秒约 40 MB 处撞到链路上限:消息越大,每秒条数越低,而 MB/秒 一直贴着上限。
  • 走回环时链路极宽,于是消息条数保持稳定(50 条连接下每秒约 102,000),而流量升到每秒 104.6 MB。这里的瓶颈是每条消息的处理开销(CPU),而不是字节数。

端到端投递

在消息体里嵌入时间戳,测量真实的投递量和端到端时延。扇出指的是投递给每一个过滤器匹配的订阅者。

环境 A —— 经网络

场景发布投递平均 / p50 / p99
10 订阅 / 10 发布 / 每秒 100 条 —— 常规986/秒9,863/秒1.17 / 1.0 / 3.6 ms
100 订阅 / 10 发布 / 不限速261,551/秒22,351/秒780 / 453 / 678 ms
100 订阅 / 10 发布 / 每秒 500 条4,934/秒12,150/秒1.87 / 1.77 / 2.83 s
1,000 订阅 / 5 发布 / 每秒 100 条494/秒178,961/秒2.29 s / 480 / 952 ms

环境 B —— 回环

场景发布投递平均 / p50 / p99
10 订阅 / 10 发布 / 每秒 100 条 —— 常规987/秒9,867/秒73 / ~0 / 555 µs
100 订阅 / 10 发布 / 不限速47,557/秒1,353,431/秒514 / 135 / 251 ms
100 订阅 / 10 发布 / 每秒 500 条3,608/秒360,834/秒17.1 / 10.5 / 71.6 ms
1,000 订阅 / 5 发布 / 每秒 100 条494/秒183,746/秒4.73 / 4.54 / 15.1 ms

分析

  • 常规负载下的时延:经网络 1.17 毫秒,走回环 73 µs。差的就是那段网络路径。
  • 负载下的分发:强力机器向订阅者投递了每秒约 135 万条消息(扇出 ×100)——比网络环境(每秒 2.2 万条)高出两个数量级,那边的代理服务器和链路都不大。
  • 在大扇出的场景里,生成端本身也成了瓶颈(所有订阅者都在一台机器的一个进程里),因此投递量的峰值应当看作代理服务器能力的下限。

两套环境对照

指标环境 A —— 网络(12 核)环境 B —— 回环(28 核)
零失败的连接数10,00030,000
连接数峰值14,879(客户端上限)约 41,000(单机上限)
接收 QoS 0(64 B)峰值271,006/秒185,012/秒
接收 QoS 1(64 B)峰值22,085/秒63,606/秒
流量上限每秒约 40 MB(链路)每秒约 105 MB(CPU)
时延(常规)中位数1.0 毫秒约 0(平均 73 µs)
扇出投递峰值22,351/秒1,353,431/秒
代理服务器日志中的错误

方法上的保留意见

  • 接收速率不能在两套环境之间直接比较:环境 B 中生成端与代理服务器共用核心,环境 A 中生成端是另一台机器。
  • 两套环境的负载都来自单台客户端机器;在某些场景下(大扇出、数万条连接),瓶颈是这台机器而不是代理服务器。
  • QoS 1 的测试客户端是同步的——因此 QoS 1 的数字低估了代理服务器的实力。
  • 测试期间没有采集代理服务器端的系统指标(CPU 与内存);服务器的状态是从它的日志(没有错误)和客户端一侧的表现推断出来的。

如何复现

每一轮测试都由同一个 loadtest 可执行文件发起;下面是三种模式的命令。

go build -o loadtest ./cmd/loadtest

# 连接容量(逐步加压与接受队列)
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode conn -levels 1000,10000,30000,50000 -hold 3s -dial-concurrency 200

# 接收吞吐
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pub -pubs 10 -qos 0 -payload 64 -duration 10s

# 端到端投递与时延
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pubsub -subs 10 -pubs 10 -qos 0 -rate 100 -payload 64 -duration 10s