MQTT实战 - 生产环境MQTT丢包终极排查,8年经验总结(六)

01、开篇:一次"丢包"引发的凌晨 3 点告警

那是去年冬天,凌晨 2:47,告警群炸了。

客户投诉:2000 台设备在线,每天稳定丢 0.3% 的告警消息。​ 0.3% 这个数字很微妙——太小不触发系统告警,太大用户直接打电话骂人,0.3% 刚好让你查了 3 天才发现。

第一天,设备组说"我发了,PublishAsync 没抛异常";第二天,平台组说"我没收到,订阅回调没触发";第三天,我去翻 EMQX 日志,发现大量 publish denied——根因是 ACL 规则顺序错误:先 deny 了 device/# 通配符,后 allow 了 device/001/alarm 具体 topic,Broker 按规则顺序匹配,直接静默丢弃。

没人看 publish denied 日志,因为默认日志级别是 INFO,而我们的采集阈值设的是 WARNING。

三天,一个 ACL 顺序问题,0.3% 的告警丢失,凌晨 3 点的告警。

这件事让我明白两件事:

  • MQTT 丢包排查不能靠猜,需要系统化的分层框架。

  • MQTT 从不主动告诉你它丢了消息——它只会安静地帮你把这条消息"当成已经处理完了"。

下面这套东西,是我 8 年踩坑后沉淀下来的:一个五层模型、六种丢包场景、一套三段定位法、一份自检清单。照着走,95% 的丢包能在 1 小时内定位到层。

02、先立一个认知——MQTT 里没有"端到端可靠"

很多人以为"我设了 QoS 2,消息就万无一失"。这是生产环境丢包事故的头号认知源头。

MQTT 的 QoS 只保证一件事:报文在「客户端 ↔ Broker」这一跳链路上的传递语义(1 次 / 2 次 / 4 次握手)。它不管:

  • 消息有没有被 Broker 主动丢弃(队列满、包过大、ACL 拒绝、磁盘满);

  • 订阅端收到之后有没有正确处理(异常、超时、反序列化失败、业务幂等误吞);

  • 消息有没有真的到达正确的 Topic(通配符不匹配 = 静默丢弃,协议不报错)。

可靠性 = 合适的 QoS + 端到端 QoS 对齐 + 业务层幂等 + 端到端对账。

缺最后一项,你永远无法证明"没丢",只能靠客户投诉来发现。

03、丢包排查的"五层模型"(总览)

丢包不是"一个问题",而是"五个层面都可能出问题"。排查顺序必须从底向上,否则你会浪费大量时间在错误的地方。

1

排查黄金法则:从两端往中间查,从现象反推根因。

为什么?因为 Broker 是最"诚实"的一层——它的所有丢弃都有指标可查(messages.dropped、mqueue、inflight)。两端的应用代码才是最会"撒谎"的——异常被吞、ACK 已回、库里没数据,你以为消息到了,其实在回调里就被吞了。

04、第一层:设备端——"我发了,但真的发出去了吗?"

4.1、客户端发送成功 ≠ Broker 收到

这是 MQTT 开发里最根深蒂固的认知误区:PublishAsync 没抛异常,就以为消息安全抵达了。实际上,这个方法的返回只代表"我这边尽力了"——至于消息到底飞没飞到 Broker,它一个字都没跟你保证。

PublishAsync 的返回语义取决于 QoS 等级,而大多数开发者从未细究过每一级到底"成功"了什么:

9

PublishAsync 返回 Success 只代表“客户端侧已受理”,不代表“已到达 Broker”,更不代表“对端已消费”。

关键区分:

  • PublishAsync 返回成功 ≠ Broker 收到消息

  • PublishAsync 返回成功 = 客户端库"接受了这条消息,承诺会尝试发送"

这两件事之间隔着:本地 socket 缓冲区 → TCP 传输 → Broker TCP 接收 → Broker 应用层处理。任何一环出问题,消息都到不了 Broker,但 PublishAsync 早就已经返回了。

核心认知:PublishAsync 返回成功是客户端视角的成功,不是系统视角的成功。QoS 0 下它只保证"数据出了门";QoS 1/2 下它保证"Broker 签收了"——但 Broker 签收后到消息最终到达订阅端之间还有无数环节可能丢。把 PublishAsync 的返回值当成"消息安全"的唯一依据,是 MQTT 项目里最危险的假设。

// ❌ 错误写法:以为没抛异常就成功了
await client.PublishAsync(msg);
// ✅ 正确写法:拿结果、判状态
var result = await client.PublishAsync(msg);
if (!result.IsSuccess)
{
    _logger.LogError("publish failed: {ReasonCode}, topic={Topic}",
        result.ReasonCode, msg.Topic);
    // 入本地重试队列(落盘,不是内存)
    _retryQueue.Push(msg);
}

4.2、发送缓冲区满导致静默丢包

MQTTnet 4.x 的ManagedMqttClient 有内部发送队列,默认满了之后的行为是丢弃新消息,不抛异常。

var options = new ManagedMqttClientOptionsBuilder()
    .WithMaxPendingMessages(2000)
    .WithPendingMessagesOverflowStrategy(
        MqttPendingMessagesOverflowStrategy.DropOldest)
    .Build();
// 监听丢弃事件
client.ApplicationMessageNotConsumedAsync += e =>
{
    _logger.LogError("MQTT消息被丢弃: Topic={Topic}, Reason={Reason}",
        e.ApplicationMessage.Topic, e.Reason);
    _metrics.Increment("mqtt.message.dropped");
    return Task.CompletedTask;
};

MQTTnet 5.0 起已移除 ManagedMqttClient(最后一个带 ManagedClient 的版本是 4.3.7.1207)。官方建议:用普通 IMqttClient,离线队列、重连、重订阅都由业务层自己实现。

4.3、异步发送未 await,异常被吞

PublishAsync 返回的是 Task,如果你不 await 也不处理 Task 的异常,异常会被 TaskScheduler.UnobservedTaskException 吞掉——生产环境表现为"消息发了但没到,日志里什么都没有"。

// ❌ 错误:fire-and-forget,异常你永远看不到
_ = client.PublishAsync(msg);
// ✅ 正确:至少用 ContinueWith 处理异常
client.PublishAsync(msg).ContinueWith(t =>
{
    if (t.IsFaulted)
        _logger.LogError(t.Exception, "publish threw");
});

4.4、线程池耗尽 & 假在线

这是两种"看起来连着、实际已经死了"的静默丢包场景:一种是应用层线程池被打爆,消息卡在 Task 队列里超时丢弃,你以为在发其实在排队;另一种是 TCP 半开连接——网线断了、WiFi 切了、NAT 超时了,但本地 TCP 栈还没感知到,IsConnected 仍然返回 true,PublishAsync 把数据写进了 socket 发送缓冲区就返回成功,数据却永远到不了对端。这两种情况客户端日志里可能没有任何异常,表现就是"连接正常但消息丢了"。

线程池耗尽的典型症状:

  • 设备运行一段时间后消息发送延迟飙升,从毫秒级变成秒级甚至分钟级

  • PublishAsync 调用后 Task 长时间不完成(排队等线程)

  • 其他依赖 ThreadPool 的逻辑也开始卡顿(心跳、重连、回调全受影响)

  • 最终 TaskScheduler 抛 OutOfMemoryException 或 TaskCanceledException

TCP 半开连接(假在线)的典型症状:

  • client.IsConnected 返回 true,但 Broker 侧已经没有这个连接

  • PublishAsync 返回成功(数据写进了本地 socket 缓冲区),Broker 没收到

  • 心跳(KeepAlive)没触发断开——因为 TCP 层没发 RST/FIN,只是中间网络已不通

  • 持续数分钟甚至数小时"假活着",直到 TCP 重传超时或应用层心跳超时

4.5、第一层自检清单

05、第二层:Broker 层——"我收到了,但我没存/没转"

5.1、ACL 规则顺序错误,静默拒绝发布

这就是开篇那次凌晨 3 点告警的根因——ACL 匹配是"第一条命中即停止",顺序写反一条,整个告警通道静默失效,而你直到业务方打电话才知道。

ACL 规则引擎按文件顺序从上到下逐条匹配,一旦命中就立即返回,不会继续往下看。这意味着:如果你先写了一条宽泛的 deny,后面再写具体的 allow,那条 allow 永远执行不到——但它不会报错、不会告警、日志里什么都没有。设备端 PublishAsync 返回成功(MQTT 协议层面 Broker 收到了),但消息被 ACL 拦截后直接丢弃,订阅端永远收不到。

# ❌ 错误:先 deny 通配符,后 allow 具体
{deny, all, subscribe, ["device/#"]}.
{allow, all, publish, ["device/001/alarm"]}.
# ✅ 正确:具体规则在前,通配符在后
{allow, all, publish, ["device/001/alarm"]}.
{deny, all, subscribe, ["device/#"]}.

ACL 拒绝默认不打 ERROR 日志,务必把 acl.deny_action 设为 ignore 并开启拒绝审计。

5.2、连接数/消息速率达到 Broker 上限

Broker 不是无限资源:连接数撞到 max_connections、建连速率撞到 max_conn_rate、入站消息撞到 messages_rate/bytes_rate 时,EMQX 不会“排队等你”,而是拒连、限流、SUBACK 返回 0x97、QoS1/2 重传堆积、甚至新连接被 defer/refuse。客户端看到的现象就是:连不上、连上了发不出、消息“发了没到”、消费端收不全——很容易被误判成“网络丢包”。

# 看集群/节点状态
emqx ctl cluster status
emqx ctl status
# 看每个 listener 的 max_conns / current_conn
emqx ctl listeners
# 典型输出里重点看:
#   max_conns     -> 该监听器最大并发连接
#   current_conn  -> 当前已占用的连接数
#   shutdown_count-> 客户端被踢/断开统计
# 看 Broker 级统计:连接数、会话数、订阅数、messages received/sent
emqx ctl broker
emqx ctl broker stats

5.3、Topic 限流 & 磁盘/内存高水位

Broker 不是"收到多少就能处理多少"——当内存、磁盘或 Topic 级限流触发时,EMQX 会静默丢弃消息或拒绝新连接,而客户端 PublishAsync 可能仍然返回成功。这是生产环境"间歇性丢包、高峰期加重"的典型来源,但往往被误判为网络抖动。

Broker 的 messages.dropped 指标会记录限流丢弃。EMQX 内存高水位默认 70%,触发后拒绝新消息。Dashboard 看资源占用,超过 70% 就该告警。

5.4、集群节点间网络分区

多节点 EMQX 集群里,节点间网络抖动是最"安静"的故障——没有宕机、没有重启,但跨节点转发的消息开始静默丢失,客户端完全无感知。

EMQX 集群中,设备连接到节点 A,订阅者连接到节点 B,消息需要经过节点间 RPC/gRPC 转发。当节点间网络出现分区(network partition)——延迟飙升、丢包、闪断——转发链路中断,消息在源节点就被丢弃或堆积,不会重试、不会回退给发送端。客户端 PublishAsync 返回成功(因为节点 A 收到了),但节点 B 上的订阅者永远收不到。

5.5、丢弃事件可观测性

EMQX 提供 $events/message_dropped 和 $events/delivery_dropped 事件主题,通过规则引擎写入可查询存储,记录 message ID、client ID、丢弃原因和时间戳。没有这个维度的监控,"Broker 没报错"和"消息没丢失"之间不能画等号。

5.6、第二层自检清单

06、第三层:网络层——"TCP 以为连着,其实早就半开了"

6.1、TCP 重传与 MQTT 心跳的博弈

这是网络层最容易被低估的"双重超时"陷阱:TCP 在底下默默重传,MQTT 在应用层傻等心跳,两者节奏没对齐时,客户端可能要等几分钟才"发现"连接已经死了——期间所有 PublishAsync 都在往一个已经不可能恢复的 socket 里写数据。

TCP 有自己的重传机制(RTO,Retransmission Timeout),默认指数退避,从 200ms 起步,最大可达 120 秒(Linux 默认 tcp_retries2=15,总耗时约 15 分钟)。而 MQTT 的 KeepAlive 是应用层心跳,靠 PINGREQ / PINGRESP 判断连接存活。问题在于:KeepAlive 如果设得比 TCP RTO 还长,客户端在 TCP 已经半开的情况下仍然认为"连接正常",继续往黑洞里发消息。

6.2、NAT 网关超时掐断

这是移动物联网场景里最"准时"的丢包——设备连得好好的,每隔几分钟就"假死"一次,发消息没反应,过一会儿又自动恢复。根因只有一个:NAT 会话表过期了,中间网关把你的连接"忘了"。

移动 4G/5G、NB-IoT、公共 WiFi 环境下,设备出口都经过运营商 NAT 网关。NAT 网关维护一张"会话映射表"(内网 IP:端口 ↔ 公网 IP:端口),每个表项都有超时时间——超时后表项被清除,后续来自 Broker 的下行报文到达网关时,网关不知道该转发给谁,直接丢弃。设备端此时 IsConnected 仍为 true(本地 TCP 栈没收到 RST/FIN),PublishAsync 往 socket 写数据也返回成功(数据进了本地发送缓冲区),但 Broker 永远收不到。

各网络环境的 NAT 超时参考:

KeepAlive 必须短于链路 NAT 会话超时,否则 TCP 半开、消息进黑洞而 IsConnected 仍为 true。

8

6.3、弱网空口丢包

QoS 0 在弱网下就是"发了等于没发"——这不是夸张,是数学。​ 空口丢包率 5% 时,一条 10 字节的 MQTT PUBLISH 报文(IP头20 + TCP头20 + MQTT固定头2 + payload 10 = 52字节)单次发送成功率是 95%,看起来还行?但如果你每秒发 10 条,连续发 1 分钟(600条),全部到达的概率是 0.95^600 ≈ 4.6×10^-14——约等于零。

而弱网环境(移动 4G 切换 5G、地下车库、电梯、偏远地区)的空口丢包率可以轻松飙到 10%–30%。

6.4、TCP ZeroWindow

ZeroWindow 是 TCP 层的"流量控制反噬"——不是网络在丢包,而是接收端处理太慢,缓冲区堆满后通过 TCP 窗口通告告诉发送端"别发了",发送端只能干等。在 MQTT 场景里,表现为:连接还在、IsConnected 为 true、但消息就是发不出去,客户端 PublishAsync 卡住或超时。

6.5、排查工具

tcpdump + Wireshark 是定位"到底丢在哪一层"的终极手段:当应用日志只告诉你"消息没到"、Broker 指标看不出异常时,抓包能直接看到 TCP 层是否发生零窗口(接收端处理不过来)、重传(网络抖动/拥塞)、RST(连接被中间设备掐断)。pcap 文件可以离线反复分析,不依赖复现环境。

tcpdump -i any port 1883 -w mqtt.pcap
# Wireshark: mqtt / tcp.analysis.zero_window / tcp.analysis.retransmission

6.6、第三层自检清单

07、第四层:MQTT 协议层——"协议语义你真的懂吗?"

7.1、QoS 语义与丢包边界

QoS 只解决"链路传输"问题,不解决"业务处理"问题——这是生产环境最容易踩的认知陷阱。QoS 0 是"发完就忘",网络抖动、Broker 重启、客户端离线全部静默丢;QoS 1 保证 Broker 至少收到一次,但网络闪断会导致重复投递,消费端必须自己做幂等;QoS 2 在单条 TCP 连接内实现恰好一次,但连接断开重连后 PUBREL 丢失仍可能重复——且 QoS 2 的握手开销是 QoS 1 的 2 倍,高吞吐场景慎用。

2

QoS 2 保证不重复,不保证不丢。​发布端网络断了,PUBLISH 根本没发出去,QoS 2 也救不了。

7.2、QoS 静默降级(最高发隐形坑)

端到端实际生效 QoS = min(发布端 QoS, 订阅端 SUBSCRIBE 请求 QoS)。

这是 MQTT 里最"阴"的丢包方式——没有异常、没有日志、没有告警,消息就是没了。因为你以为自己在用 QoS 1,实际上 Broker 悄悄给你降到了 QoS 0。

也就是说:你发布时用了 QoS 1,但订阅端 SUBSCRIBE 时没声明 QoS(或声明了 QoS 0),Broker 在转发时直接把消息降级为 QoS 0 投递。没有 PUBACK、没有重传、丢了就是丢了。发布端浑然不知——PublishAsync 返回成功,因为发布端的 QoS 1 只保证"到 Broker"这一段可靠,不保证"Broker 到订阅端"。

// ❌ 错误:默认 QoS 0
await client.SubscribeAsync("device/+/alarm");
// ✅ 正确:显式声明
await client.SubscribeAsync(new MqttTopicFilterBuilder()
    .WithTopic("device/+/alarm")
    .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
    .Build());

7.3、CleanSession 导致离线消息全丢

这是 MQTT 里最"合规"的丢包——协议说清就清,不跟你商量。设备断线重连的瞬间,Broker 默默把会话里所有未投递的 QoS 1/2 消息全部销毁,而你还在指望"重连后消息会回来"。

问题本质:CleanSession=true(MQTT 3.1.1)意味着:客户端每次连接都是全新会话,断开后 Broker 立即销毁该会话的所有状态

MQTT 5.0 的改进是本质性的:CleanStart=false 告诉 Broker"我要复用之前的会话",SessionExpiryInterval 告诉 Broker"这个会话最多保留多久"。两者组合 = 离线消息有保底、有上限、不泄漏。

7.4、Retain 被覆盖 & 遗嘱 QoS 不够

是 MQTT 里两个"看起来很美、用起来很坑"的机制——Retain 让你以为 Topic 有状态,遗嘱让你以为断连必有通知。结果:状态被悄悄覆盖,遗嘱本身静默丢失。

MQTT 的 Retain 语义是覆盖式的——同一个 Topic 上,新 Retain 消息直接替换旧 Retain 消息。不是队列,不是追加,是纯覆盖。

MQTT 遗嘱(LWT, Last Will and Testament)的设计意图是:设备异常断连时,Broker 自动发布一条预设消息,通知其他客户端"这哥们挂了"。

核心认知:Retain 和遗嘱都是"看起来简单、实际暗坑多"的 MQTT 特性。Retain 的本质是"每个 Topic 的最新快照",不是消息队列、不是事件流——把它当状态机用就对了,当消息通道用就错了。遗嘱的本质是"尽力而为的断连通知",默认 QoS 0 让它连"尽力而为"都做不到——显式声明 QoS 1 + Retain,让它至少能可靠投递一次。

7.5、第四层自检清单

08、第五层:业务层——"消息到了,但你没处理"

8.1、消费逻辑抛异常被吞(占比最高)

消费端丢消息的第一大原因不是网络,不是 Broker,而是你自己的 try/catch 写漏了。MQTT 客户端回调里如果异常逃逸到 ApplicationMessageReceivedAsync 之外,MQTTnet 默认行为是吞掉异常、继续跑——Broker 已经发了 PUBACK,消息从"已投递"变成"已丢失",日志里可能连个痕迹都没有。更隐蔽的是:即使你写了 try/catch 但 catch 块里只写了 Log.Error 就完事,消息既没重试也没落死信,等于确认了一次失败的消费。

// ✅ 正确:try-catch + 死信落盘
client.ApplicationMessageReceivedAsync += e =>
{
    try { ProcessMessage(e.ApplicationMessage); }
    catch (Exception ex)
    {
        _logger.LogError(ex, "处理失败: {Topic}", e.ApplicationMessage.Topic);
        _deadLetter.SaveAsync(e.ApplicationMessage, ex);
    }
    return Task.CompletedTask;
};

8.2、幂等误判 & 日志缺失

这是 MQTT 可靠性工程里最"冤"的丢包——消息明明到了,却被你自己的代码"吃"掉了;出了事翻日志,发现只有 INFO 消息已处理,连个 payload 摘要都没有。

MQTT QoS 1/2 保证的是"至少一次"(At Least Once),不是"恰好一次"。网络抖动、重连重传、Broker 重试,都会让同一条业务消息被投递多次。消费端必须做幂等——但幂等本身就是一个高风险的"二次丢包源"。

核心认知:幂等不是"加个 Redis SETNX"就完事了——去重键选错比不加幂等还危险,因为它静默吞消息,没有异常、没有日志、没有告警。而日志缺失让这个问题雪上加霜:你甚至不知道消息是被幂等拦掉的还是根本没收到。解决方案只有一条:去重键用业务唯一 ID(设备端生成、端到端贯穿),日志结构化到每条消息的生命周期每个节点。

8.3、第五层自检清单

09、三段定位法:30 分钟把丢包钉在某一段

别一上来翻全链路日志,用旁路对照二分:

9.1、第一步|旁路订阅判断 Broker 有没有收到

这是排查丢包最快的"一锤定音"手段:在 Broker 上开一个独立的探针订阅(用不同 ClientId、不同连接),订阅同样的 Topic 通配符,跟业务消费者"并听"。如果探针收到了但业务端没收到,问题 100% 不在网络、不在发送侧,而在 Broker 订阅路由 / 消费端处理;如果探针也没收到,说明消息压根没进 Broker,回头查设备端发送和中间网络。这一步能把排查范围从"五层全查"瞬间缩小到"两层以内",每次丢包事故第一件事就做这个。

# 旁路探针:独立 ClientId、QoS 2、持续订阅、输出到日志
mosquitto_sub -h broker -p 8883 -u probe -P '***' \
  -i probe_$(date +%s) -q 2 -t 'device/+/alarm' -v >> probe.log

在订阅端挂一个探针(独立 ClientId + 相同 Topic),对比“探针是否收到”,30 秒缩小排查范围。

3

9.2、第二步|发送侧 seq 对账

用数据说话,而不是靠猜。每条消息在发送侧生成唯一 seq(ULID / Snowflake),发送前落 t_sent 表,收到 PUBACK/PUBREC 后更新状态为 ack_ok。这样你能精确区分三种丢失:

send_ok 但 Broker messages.received 对不上 → 网络/中间设备丢

send_ok 但永远没收到 PUBACK → 链路层丢(QoS 1/2 未确认)

ack_ok 但业务端没消费到 → Broker 到消费端丢

-- 发送侧发出但未确认的消息
SELECT device_id, topic, seq, created_at
FROM t_sent
WHERE ack_status = 'pending'
  AND created_at < NOW() - INTERVAL 30 SECOND;

用三组指标差值,把“丢在哪一段”从玄学变成可计算的事实;差值持续 > 0 即丢包。

4

9.3、第三步|端到端 diff(兜底)

前两步缩小了范围,这一步给出精确数字:发送侧和接收侧各自落库的 seq 做 LEFT JOIN,差集就是"发出去了但没消费到"的消息。这是所有手段里唯一能量化丢包率的方法——不是"好像丢了几条",而是"过去 5 分钟丢了 23 条,丢包率 0.17%"。当丢包是间歇性、低概率发生时,靠抓包和探针很难复现,但 diff SQL 能持续跑、能接告警、能回溯任意时间窗口。

-- 发送侧 seq 表 LEFT JOIN 接收侧 seq 表,差集 = 丢失消息
SELECT s.topic, s.device_id, s.seq
FROM t_sent s
LEFT JOIN t_recv r ON r.seq = s.seq AND r.device_id = s.device_id
WHERE r.seq IS NULL
  AND s.ts < NOW() - INTERVAL 5 MINUTE;

这条 SQL,就是"丢包"从玄学变成事实的全部秘密。

10、排查工具箱:6 个必杀技

这 6 个工具覆盖了从"发现问题"到"定位根因"的全链路——$SYS 主题看宏观指标、结构化日志做链路追踪、tcpdump 下钻到 TCP 层、压测复现偶发问题、旁路订阅隔离故障层、client trace 精准抓单个设备的协议交互。排障时不需要全部上,按"先宏观后微观、先旁路后抓包"的顺序选 2~3 个就够定位。

从实时监控到协议级抓包,六件套覆盖“定位—复现—隔离—追踪”全流程。

5

11、真实案例复盘:5 个经典故障

这 5 个案例覆盖了五层模型中的全部层级,每一个都是"看起来像网络丢包、实际根因完全不同"的真实生产事故。它们共同说明一件事:丢包排查不能靠直觉,必须按链路逐层推进——你以为的"网络问题"大概率是 ACL 配错,你以为的"QoS 不够"其实是 CleanSession 没配对,你以为的"丢包"可能只是乱序。

每一条都是凌晨 3 点的告警:现象 → 排查路径 → 根因 → 修复,四步锁死。

6

12、上线前自检清单:17 条

这 17 条是从 8 年生产事故里一条条提炼出来的"保命清单"——每一条背后都对应至少一次线上故障。上线前逐条打勾,比任何压测都能提前拦住丢包隐患。建议打印出来贴在工位,每次发版前过一遍,养成肌肉记忆。

打印出来贴在工位上,上线前逐条打勾;每一条背后都是一次凌晨 3 点的告警。

7

13、最后

我整理了一份《MQTT丢包排查清单》,包含:

  • 五层排查模型总览(设备端、Broker、网络、MQTT协议、业务层,30秒定位问题方向)

  • 24条逐层排查清单(每层检查项 + 最高发事故,附勾选框,逐条过不遗漏)

  • 17项上线前自检清单(精简版,打印贴工位,上线前逐条打勾)

  • 5个真实案例复盘(ACL静默丢弃、CleanSession、缓冲区满、连接数上限、消费乱序)

私信「MQTT丢包排查清单」直接领取排查清单主体,无套路。

做物联网 MQTT 的朋友可以一起交流。后面我会在这个号持续更新这个系列,也会穿插一些软考架构师备考、技术管理转型、嵌入式学习的记录。

posted @ 2026-10-23 07:06  IT规划师  阅读(76)  评论(0)    收藏  举报