11 KiB
本地 MQTT 并发与稳定性测试方案
更新日期:2026-07-14
本文档用于说明 Banban / TalkingQ 后端在不调用真实外部服务的前提下,如何做本地全流程并发和稳定性测试。测试重点是 MQTT 设备接入、消息解析、服务端回包、GPS 查询响应、告警处理、数据库落库、后端健康状态和资源变化。
1. 测试目标
1.1 并发度
并发度用于回答系统同时承载多少设备连接和消息流量。
本项目里主要看:
- 同时在线的 MQTT 设备客户端数量。
- 设备状态上报
msg_id=000的吞吐。 - 告警事件
msg_id=010的吞吐和落库。 - 服务端 GPS 查询
msg_id=001的下发和设备响应。 - 服务端事件响应
event_resp是否完整返回。
1.2 稳定性
稳定性用于回答系统在持续运行一段时间后是否保持正确和健康。
本项目里主要看:
- 设备连接是否持续保持。
- 上行消息和服务端回包是否丢失。
- 数据库写入是否持续成功。
- 后端健康检查是否持续返回
200。 - 错误日志是否新增。
- CPU、内存、数据库连接数、MQTT 连接数是否持续爬升或堆积。
2. 测试边界
2.1 本次覆盖
- 本地 FastAPI 后端。
- 本地 MQTT broker。
- 本地 MySQL。
- 真实 MQTT 客户端连接,每个模拟设备一个 MQTT client。
- MQTT 主流程:
device/{device_id}/event上行。device/{device_id}/event_resp下行响应。device/{device_id}/command服务端命令。device/{device_id}/response设备响应。
- 状态上报、告警、GPS 查询、异步持久化、告警落库。
2.2 本次不覆盖
- 线上 MQTT broker。
- 线上服务器网络和 TLS。
- 真实高德逆地理编码调用。
- 真实短信发送。
- 真实设备固件网络抖动。
- 断线重连风暴。
- 慢数据库、数据库断连、外部服务长超时。
这些场景需要单独做线上灰度压测或故障注入测试。
3. 外部服务隔离策略
本地全流程测试要验证主流程,但不能依赖真实外部服务。
推荐策略:
- GPS 响应使用无效坐标
lat=0,lng=0。 - 后端应忽略无效 MQTT 定位,不调用高德解析。
- 告警测试账号不配置可用家长手机号。
- 后端短信链路应走“无手机号跳过”,不调用短信服务。
- 不使用真实 COS 上传、微信模板消息或其他公网依赖作为通过条件。
通过标准不是“外部服务成功”,而是“外部服务不可用或被跳过时,MQTT 主流程、回包和落库不受影响”。
4. 测试环境
4.1 本地服务
在 talkingq-url 目录运行:
.venv/bin/amqtt
启动后本地 MQTT broker 监听:
127.0.0.1:1883
启动后端:
.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8080
健康检查:
curl -sS -o /dev/null -w 'openapi_http=%{http_code} time=%{time_total}\n' \
http://127.0.0.1:8080/openapi.json
预期:
openapi_http=200
4.2 配置要求
talkingq-url/.env 应指向本地服务:
DB_HOST=127.0.0.1
DB_PORT=3306
TALKINGQ_MQTT_BROKER=127.0.0.1
TALKINGQ_MQTT_PORT=1883
4.3 测试设备数据
建议使用专门的压测设备 ID,避免污染真实设备:
TalkingQ_LOAD000001
TalkingQ_LOAD000002
...
TalkingQ_LOAD000500
测试前确认:
device_auth有对应设备。device_bindings有对应绑定。- 告警落库使用的
owner_user_id、child_id不超过当前数据库字段范围。
检查示例:
SELECT COUNT(*) FROM device_auth WHERE device_id LIKE 'TalkingQ_LOAD%';
SELECT COUNT(*) FROM device_bindings WHERE device_id LIKE 'TalkingQ_LOAD%';
5. 压测脚本要求
压测脚本应满足以下行为:
- 每个设备使用一个独立 MQTT client。
- 每个 client 订阅:
device/{device_id}/event_respdevice/{device_id}/command
- 每个 client 周期性发布
msg_id=000设备信息。 - 按固定比例发布
msg_id=010告警。 - 收到服务端
msg_id=001GPS 查询后,回复device/{device_id}/response。 - 默认 GPS 模式使用
invalid,返回0,0,避免真实高德调用。 - 统计发送数、回包数、GPS 命令数、GPS 响应数、发布延迟。
状态上报 payload 示例:
{
"msg_id": "000",
"data": {
"id": "TalkingQ_LOAD000001",
"power": 79,
"signal": 4,
"version": "load-client-1.0.0",
"voice": 60,
"imei": "990000000000001",
"device_type": 2,
"latitude": 0,
"longitude": 0,
"coord_type": "gcj02"
}
}
告警 payload 示例:
{
"msg_id": "010"
}
GPS 响应 payload 示例:
{
"msg_id": "001",
"status": "success",
"data": {
"latitude": 0,
"longitude": 0,
"coord_type": "gcj02",
"accuracy_m": 10,
"source": 1,
"battery_pct": 80
}
}
6. 推荐测试阶段
6.1 冒烟测试
目的:确认脚本、broker、后端、DB 基本可用。
建议参数:
20 个 MQTT client
1 轮状态上报
每 10 台设备触发 1 次告警
settle 5 秒
通过标准:
- client 连接成功数等于请求数。
sent_total == event_resp_total。resp_000 == sent_000。resp_010 == sent_010。command_001 == gps_response_sent。
6.2 短时并发测试
目的:验证较高设备规模下的瞬时吞吐。
建议参数:
500 个 device_id
3 轮状态上报
每 10 台设备触发 1 次告警
settle 20 秒
通过标准:
- 所有事件收到
event_resp。 - 告警落库数量与发送数量一致。
- 健康检查仍返回
200。 - 新增错误日志为
0。
6.3 真实连接稳定性测试
目的:验证 500 个独立 MQTT client 持续在线时,主流程是否稳定。
建议参数:
500 个 MQTT client
持续 30 分钟
每 15 秒每台设备上报一次 msg_id=000
每 60 秒每 20 台设备触发一次 msg_id=010
GPS mode = invalid
settle 20 秒
通过标准:
- 500 个 client 全部连接成功。
- 连接数在测试期间保持稳定。
sent_total == event_resp_total。sent_000 == resp_000。sent_010 == resp_010。command_001 == gps_response_sent。- 30 分钟内后端健康检查持续返回
200。 - 新增错误日志为
0。 - MySQL 连接数没有持续堆积。
- 后端和 broker RSS 没有持续单调增长。
6.4 长时稳定性测试
目的:把 30 分钟结论提升为更可靠的上线前参考。
建议参数:
500 个 MQTT client
持续 2 小时
或过夜 8-12 小时
保持 15 秒状态上报和 60 秒告警节奏
增加观察项:
- 日志滚动后是否还能正常写入。
- 定时任务是否干扰 MQTT 主流程。
- 数据库表增长后查询是否变慢。
- 后端进程 RSS 是否长期爬升。
- broker 是否出现连接残留。
7. 监控指标
建议每 60 秒记录一次:
sample
elapsed_s
backend_cpu
backend_mem
backend_rss_kb
broker_cpu
broker_mem
broker_rss_kb
mqtt_established
mysql_threads
mysql_processes
http_code
http_time_s
new_error_lines
采集方式示例:
ps -o %cpu=,%mem=,rss= -p <backend_pid>
ps -o %cpu=,%mem=,rss= -p <broker_pid>
lsof -nP -iTCP:1883 -sTCP:ESTABLISHED
curl -sS -o /dev/null -w '%{http_code} %{time_total}' http://127.0.0.1:8080/openapi.json
MySQL 连接数:
SHOW STATUS LIKE 'Threads_connected';
SHOW PROCESSLIST;
错误日志过滤:
rg -n "(ERROR|failed|DataError|Out of range|告警地址解析失败|当前位置地址解析失败|amap|高德)" \
talkingq-url/logs/talkingq_YYYY-MM-DD.log
8. 本次执行结果
执行日期:2026-07-14
8.1 单元测试
86 passed
8.2 500 客户端 30 分钟稳定性测试
测试参数:
devices_requested=500
duration_seconds=1800
info_interval_seconds=15
alarm_every=20
alarm_interval_seconds=60
gps_mode=invalid
结果:
elapsed_seconds=1820.053
client_ready_success=500
client_ready_failed=0
sent_total=60750
event_resp_total=60750
sent_000=60000
resp_000=60000
sent_010=750
resp_010=750
command_001=750
gps_response_sent=750
publish_latency_ms_avg=29.301
publish_latency_ms_p95=48.727
8.3 数据库验证
device_settings_load=500
device_alarm_events_load_total=877
device_alarm_events_load_last_40m=750
child_location_current_load=0
mysql_threads_connected=6
说明:
device_alarm_events_load_last_40m=750与本轮告警发送数一致。child_location_current_load=0是预期结果,因为 GPS 使用0,0,后端忽略无效定位。
8.4 资源变化
后端:
压测前 RSS 约 129.9MB
500 客户端连接后 RSS 约 131-132MB
中途回落到约 120-121MB
结束后约 121.6MB
CPU 基本 0.1%-0.2%
MQTT broker:
压测前 RSS 约 43.9MB
500 客户端连接后 RSS 约 73-75MB
后半程稳定在约 69-70MB
结束后约 70.1MB
CPU 基本 0.0%-0.3%
连接数:
压测前 mqtt_established=2
压测中 mqtt_established=1002
压测后 mqtt_established=2
说明:本机 loopback 下 lsof 会看到连接两端,1002 约等于 501 条 MQTT 连接,包括 500 个设备客户端和 1 条后端服务连接。
8.5 日志验证
测试窗口内新增错误日志:
0
未发现:
ERRORfailedDataErrorOut of range告警地址解析失败当前位置地址解析失败amap高德
9. 本次结论
本地环境下,当前项目已验证可以稳定承载:
500 个独立 MQTT 客户端
持续 30 分钟
60750 条事件完整回包
750 条告警完整落库
750 次 GPS 查询完整响应
测试期间:
- 后端健康检查持续
200。 - 新增错误日志为
0。 - MySQL 连接没有堆积。
- 后端和 broker 内存没有持续爬升。
- 外部服务未调用时,MQTT 主流程不受影响。
因此可以判断:本地单实例下,500 台设备级 MQTT 连接和当前消息节奏的 30 分钟稳定性通过。
10. 风险和下一步
10.1 当前结论不能直接等同线上容量
原因:
- 本地 broker 是
amqtt,不是线上 broker。 - 本地网络没有公网延迟、TLS、丢包、移动网络抖动。
- 高德和短信没有真实调用。
- 测试时长是 30 分钟,不是 24 小时。
- 没有模拟断线重连风暴。
10.2 建议补充测试
上线前建议继续做:
- 500 客户端持续 2 小时。
- 500 客户端过夜 8-12 小时。
- 断线重连测试:一次性断开 20%、50%、100% 客户端后重连。
- 慢 DB 测试:模拟 DB 写入变慢,确认 MQTT 回包和主流程是否可控。
- 高德失败测试:模拟超时、HTTP 500、无效 Key,确认地址解析失败不影响告警落库。
- 短信失败测试:模拟短信服务超时或失败,确认告警主流程不被拖垮。
- 线上灰度测试:在真实 broker 和服务器规格下从 50、100、300、500 逐步提升。
11. 通过标准汇总
一次测试可判定通过,需要同时满足:
- 客户端连接成功数等于目标数。
sent_total == event_resp_total。sent_000 == resp_000。sent_010 == resp_010。command_001 == gps_response_sent。- 告警新增落库数量等于
sent_010。 - 健康检查全程
200。 - 新增错误日志为
0。 - MySQL 连接数没有持续增长。
- 后端和 broker RSS 没有持续单调增长。
- 测试结束后 MQTT 连接数回落到基线。