侧边栏壁纸
  • 累计撰写 20 篇文章
  • 累计收到 1 条评论

Python对接MQTT频繁丢消息?我踩了3个易忽略的细节坑

2026-9-8 / 0 评论 / 14 阅读

你有没有遇过Python写的MQTT订阅脚本,日志明明显示连接成功,就是随机丢10%左右的设备上报消息,重启脚本又能正常半小时?上周我搭车间设备数据采集链路的时候就卡了整整一下午在这个问题上。

一开始以为是broker配置的锅

最开始排查方向全错了。先是把EMQX的消息队列长度拉到10万,开了消息持久化,又把所有订阅主题的QoS都改成1,跑了20分钟压测,丢包率一点没降。甚至换了个测试用的Mosquitto broker,问题还是复现,直接排除了服务端的问题。

抓包才揪出根因

在服务器上挂tcpdump抓1883端口的流量,发现异常点:每次客户端发完心跳PINGREQ之后,broker连续推了十几条消息过来,本地脚本压根没回PUBACK。

回头翻代码才反应过来,我之前图省事,在on_message回调里直接写了SQLite入库逻辑,赶上磁盘IO抖动的时候,单条消息处理要卡200ms以上。而paho-mqtt默认是单线程跑网络循环,回调一堵,整个消息接收流程直接卡死,内部队列满了之后新消息直接被丢弃。

改完这几个配置零丢包

  • 网络循环别用loop_forever(),换成独立线程启动,不阻塞业务逻辑:
    client = mqtt.Client(client_id="sub_collect_01", clean_session=False)
    client.connect("192.168.1.20", 1883, keepalive=60)
    # 启动独立网络线程,和业务逻辑隔离
    client.loop_start()
  • 调整客户端内部消息队列上限,默认最大积压20条消息根本不够用:
    client.max_inflight_messages_set(200)
    client.max_queued_messages_set(10000)
  • QoS必须发布端、订阅端同时配置,光改单边等于白设。on_message里的阻塞逻辑(写库、调接口)全扔到线程池处理,绝对不能堵回调。
重点提醒:paho-mqtt的所有回调都是跑在网络线程里的,只要单次回调执行时间超过keepalive周期的1/4,就算日志显示连接正常,也大概率会出现消息积压甚至假死。

改完之后连续跑了72小时压测,累计32万条设备消息零丢失,之前浪费的一下午全是因为想当然在回调里写阻塞逻辑踩的坑。

评论一下?

OωO
取消