用 Proxy 注入并验证 MQTT 延迟
创建隔离的 Proxy,让 MQTT 测试流量经过它,注入 500 ms 下行延迟,对比 Trace 证据,再删除 Toxic 并证明恢复。
- 作者:
- Mqttable
- 更新:
本页目录8 个章节
加载中...
创建隔离的 Proxy,让 MQTT 测试流量经过它,注入 500 ms 下行延迟,对比 Trace 证据,再删除 Toxic 并证明恢复。
加载中...
本教程完成一个完整实验:先证明 MQTT 路径在无故障时正常,再注入 500 ms 下行延迟、观察结果、删除故障并证明恢复。尚未安装 Mqttable 的读者可以通过截图理解整条工作流;实际执行只需要 Mqttable 和一个你拥有或获授权测试的 MQTT Broker。
Proxy 名称、Client ID、Topic 和 Payload 都由本教程提供并由用户填写,Mqttable 不会自动生成这些值。
MQTTABLE CONNECTIONS
Publisher + Subscriber
连接 127.0.0.1:3883
MQTTABLE PROXY
Docs Proxy Demo
监听 127.0.0.1:3883
上游 127.0.0.1:1883
已授权 MQTT BROKER
上游目标
127.0.0.1:1883
从工作区侧边栏进入代理。全新的 Runtime 会显示暂无代理和创建代理。

从空 Proxy 工作区开始,避免已有监听端口、Toxic 或 Trace 污染本次实验。
Mqttable 不内置 MQTT Broker。本实验使用已经在 127.0.0.1:1883 监听、且获授权测试的 Broker。不要改成公共、共享或生产 Broker:添加 Toxic 后,所有经过此 Proxy 的真实流量会立即受到影响。
选择创建代理并填写:
Docs Proxy Demo127.0.0.1:3883TCP127.0.0.1:18830 秒表单默认监听 127.0.0.1:1883,会与本教程的 Broker 冲突;默认上游是公网 broker.emqx.io:1883。必须同时替换这两个值。初始延迟控制 Proxy 启动或该值变化后,Periodic Toxic 要等待多久才开始;本教程使用 Always On,不需要额外等待。

专用回环 Listener 把故障实验流量与 Broker 的直连端点隔离开。
创建后确认:已选中 Docs Proxy Demo;路由为 127.0.0.1:3883 → 127.0.0.1:1883;状态是代理运行中;连接数为 0;Toxic 区域为空。

Listener 已运行只是配置证据,还不能证明 Client 已经能通过 Proxy 到达 Broker。
如果状态不是运行中,请停止。监听端口冲突、上游不可达或地址格式错误都必须在添加 Toxic 前解决。
授权 Broker 继续监听 1883。然后打开 Connections,创建一个指向 Proxy Listener、而不是直连 Upstream 的专用 Broker 工作区:
Docs Proxy Routemqtt127.0.0.13883选择测试连接;只有 MQTT 3.1.1 和 MQTT 5.0 都显示连接正常后,才选择创建。界面显示的 Endpoint 必须是 mqtt://127.0.0.1:3883;如果是 1883,就绕过了 Proxy。
在 Docs Proxy Route 中创建并连接 Mqttable 内置 MQTT 5 Client:
subscribermqttable-docs-proxy-subscriber-01在已连接的 subscriber 行选择添加订阅,Topic 填写 mqttable/docs/proxy/temperature,QoS 选择 1。清理前保持这个 Mqttable Client 已连接。Client ID 和 Topic 都由本教程提供,不是 Mqttable 自动生成的值。
只有 Proxy 出现一个下游连接,且 Trace 依次显示成功的 SUBSCRIBE → 发送 与 SUBACK ← 接收 后才能继续。

Connection 行证明 Client 从 3883 进入;SUBACK 证明上游 Broker 接受了 Topic Filter。
如果 Subscriber 能通信,但 Proxy 仍显示 0 个连接且 Trace 为空,通常说明它绕过了 Proxy,仍在直连 1883。先修正 Client 端口。
回到 Connections → Docs Proxy Route,创建第二个 Mqttable 内置 MQTT 5 Client:
publishermqttable-docs-proxy-publisher-01连接 publisher。在 Composer 中选择它,Topic 填写 mqttable/docs/proxy/temperature,QoS 选择 1,Retain 保持关闭,Payload 填写:
{"phase":"baseline","temperature":21.5}
只选择一次发送,收起 Composer,再返回 Proxy。点击发送不等于成功。必须同时看到 Publisher 的 PUBLISH → 发送、PUBACK ← 接收,Subscriber 的 PUBLISH ← 接收、PUBACK → 发送,并在 Trace 中看到相同的 phase=baseline Payload。

PUBACK 只证明 Broker 接受发布;独立 Subscriber 的 PUBLISH 与 PUBACK 才证明消息经过 Proxy 完成端到端投递。
本次采集中,Publisher 在 22:34:07.630 发送 PUBLISH,Subscriber 在 22:34:07.632 收到。这个 Trace 间隔只用于本实验内的对比,不是 Benchmark 结果。回到 Connections,断开 publisher,但保持 subscriber 已连接,让下一次采样包含一次新的 Publisher 连接。
选择添加 Toxic。Mqttable 当前在同一 Proxy 工作区提供 6 种网络 Toxic 和 3 种 MQTT-aware Toxic。

Mqttable 把通用链路故障和 MQTT 专用的断开、保活与重复消息行为放在同一工作区。
选择延迟并填写:
100%500 ms0 ms下行表示 Broker → Client。它会延迟 Publisher 收到的 CONNACK、PUBACK,也会延迟 Subscriber 收到的 PUBLISH。故障强度是 Toxic 被应用的概率,不是额外的延迟百分比。

选择添加前重新核对隔离目标和每个字段;Engineer UI 对此操作没有 Dry Run。
确认没有无关 Client 依赖 127.0.0.1:3883 后,才选择添加。
回到 Connections → Docs Proxy Route,重新连接 publisher。状态变为已连接后,在 Composer 中保持相同 Topic、QoS 1 和 Retain 关闭,只发送一次相同长度的故障 Payload:
{"phase":"fault-on","temperature":21.5}
Toxic 行证明的是配置,不是影响结果。用 Trace 时间戳检查 Publisher CONNECT → 发送 到 CONNACK ← 接收、Publisher PUBLISH → 发送 到 Subscriber PUBLISH ← 接收 的间隔,以及 Subscriber 收到的 Payload 和 PUBACK。

活动 Toxic 与 MQTT 行在同一 Workbench 中保留故障条件、受影响报文和最终投递 Payload。
Trace 中 CONNECT 到 CONNACK 约为 560 ms,Publisher PUBLISH 到 Subscriber PUBLISH 约为 551 ms。不要期待每个间隔都恰好等于 500 ms:协议和处理开销会叠加在注入延迟周围。需要具有统计意义的延迟或容量结果时,使用 Benchmark。删除 Toxic 前再次断开 publisher。
选择 exact latency_downstream 行末尾的删除按钮。Toxic 区域应立即变空。不要用停止 Proxy 代替删除:停止 Listener 不能证明这条持久故障配置已经被移除。
回到 Connections,重新连接 publisher,并在 Composer 中保持相同 Topic、QoS 1 和 Retain 关闭,只发送一次相同长度的恢复 Payload:
{"phase":"recovery","temperature":21.5}

恢复需要一次新的发布:Toxic 为空、recovery Payload 已投递、时间间隔重新接近基线。
恢复 Trace 中 CONNECT 为 22:36:03.935、CONNACK 为 22:36:03.936,Publisher 与 Subscriber 的 PUBLISH 都是 22:36:03.936,已经回到接近无故障基线的间隔。如果 Client 仍持续重连、重复消息仍出现或旧积压仍在排空,就不能判定恢复完成。
只清理本教程创建的 Connections 工作区 Docs Proxy Route、其中两个内置 Client ID、Proxy Docs Proxy Demo、mqttable/docs/proxy/temperature Topic 流量,以及此时应已不存在的 latency_downstream Toxic。
打开 Connections,进入 Docs Proxy Route 的 Broker 设置并选择删除。只有确认对话框点名这个专用工作区时才继续。该操作会断开 mqttable-docs-proxy-publisher-01 和 mqttable-docs-proxy-subscriber-01,不会删除 1883 上的授权 Upstream Broker。返回 Proxy,等待连接数回到 0,再选择停止代理。

测试 Client 已退出,Listener 已停止,也没有剩余 Toxic;现在可以删除持久工作区。
当前版本需要先退出 Zen 模式,才能使用 Workspace Tab 上的删除操作。只删除 Docs Proxy Demo;确认对话框会说明删除将停止 Proxy 并移除其 Trace 数据。删除后重新进入 Zen 模式,验证最终空状态。

成功通知和空工作区共同证明教程 Proxy、Listener、Trace、Connection 与 Toxic 已不再残留。
清理过程中不要清空其他 Proxy 的 Trace、删除共享 Broker 配置或停止无关的本地 Broker。
完成第一次延迟实验后,每次只替换一种 Toxic:
变化为匹配报文增加延迟和可选抖动。
证据Trace 时间间隔与 Client 耗时。
主要风险Timeout 与 ACK 延迟。
变化限制一个方向的吞吐。
证据传输耗时、积压与 Trace 进度。
主要风险大 Payload 变慢或不完整。
变化按百分比丢弃指定 MQTT 报文。
证据缺失报文、重传或超时。
主要风险ACK 丢失与重复投递。
变化立即或延时发送 TCP RST。
证据断链与有边界的重连证据。
主要风险未确认消息与重连风暴。
变化把流量拆成小块并增加间隔。
证据应用行为,必要时结合 PCAP。
主要风险解码边界与慢读取。
变化累计字节达到上限后关闭连接。
证据Trace 在配置边界停止。
主要风险部分传输与会话中断。
变化按时间、报文数或概率注入 MQTT DISCONNECT。
证据Reason Code 与后续 Client 状态。
主要风险真实 Session 与重连副作用。
变化丢弃或延迟 PINGREQ/PINGRESP。
证据Keep Alive Timeout 与状态变化。
主要风险半开连接与延迟发现故障。
变化生成额外 PUBLISH,可设置 DUP 标志。
证据一条源消息对应多个 Subscriber 行。
主要风险缺少幂等时触发重复业务动作。
每种 Toxic 还包含 Direction 和 Toxicity。始终开启会持续生效;周期性增加 Duration、Break、Repeat 和 Offset,Repeat = 0 在界面显示为 ∞。第一次不要叠加多个 Toxic,否则 Latency 与 Packet loss 同时出现时很难归因 Timeout。
当前 Proxy Profile 包含 TCP、TLS、WS 和 WSS,下游与上游使用同一个选中 Profile。TLS 和 WSS 的证书配置不在本回环教程范围;不要使用占位凭据并声称路由已经成功。
Free 最多支持 3 个 Proxy Workspace,Pro 取消该数量限制。Remote Runtime 需要 Pro;此时 127.0.0.1 指 Runtime 所在主机,不一定是用户桌面。
Mqttable 与 Shopify Toxiproxy 采用相同的“先让流量经过代理,再改变链路”思路,但不兼容 Toxiproxy CLI 或 HTTP API。它的独特价值是把 MQTT-aware Toxic、下游连接、精确配置和协议 Trace 证据放进同一个 Workbench。
Proxy Trace 证明 MQTT 协议与路由行为,但不能证明 TCP 分片、重传或完整抓包。需要包级证据时使用 PCAP 分析,也可以把无故障和故障消息整理为可重复的 Replay Scenario。