
跨城机房,PoE 网口温湿度变送器经边缘网关接入腾讯云 IoT Explorer。运行一周后运维反馈:
MQTT connection lost, reconnecting...,重连后退避,补传本地缓存 初步怀疑云侧限流或证书过期,排除后定位到本地链路层 + PoE 供电耦合。
变送器(PD) ─Cat5e─ PoE交换机(柜内) ─内网─ 边缘网关(Mini PC)
│
MQTTS → 腾讯云 IoT Explorer边缘网关日志加时间戳 + TCP 状态跟踪:
ss -tnp | grep 8883
dmesg -T | grep -i 'link\|phy'
tcpdump -i eth0 -nn 'tcp port 8883' -w /tmp/mqtt.pcap &
tcpdump -i eth0 -nn 'tcp port 502' -w /tmp/modbus.pcap &Wireshark 回传分析:断链前出现:
同期交换机日志:PoE controller temperature alarm、port power denied、PD powered down。
关键线索:掉线窗口与柜内空调压缩机启停、白天负载升高重合。示波器/电源记录仪挂 PoE 交换机一次电源输入 + 端口输出:
paho-mqtt(Python)配置:
client = mqtt.Client(...)
client.enable_logger()
client.keepalive = 60问题点:
轮询循环伪代码原实现:
while True:
for s in sensors:
c = ModbusTcpClient(s.ip, 502, timeout=1)
c.connect() # 每次新建
r = c.read_holding_registers(0,3,unit=1)
publish(...)
time.sleep(poll_interval)PoE 复位后变送器 IP 不变但 TCP 栈重启,半开连接堆积;pymodbus 默认不清理 socket,fd 泄漏。网关进程内存涨,GC 停顿,MQTT 循环被阻塞,云端心跳未按时发 → 服务端断开。
net.ipv4.tcp_keepalive_time=120
net.ipv4.tcp_keepalive_intvl=15
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_user_timeout=60000import paho.mqtt.client as mqtt
from paho.mqtt.enums import CallbackAPIVersion
client = mqtt.Client(CallbackAPIVersion.VERSION2,
client_id=CLIENT_ID, transport="tcp")
client.tls_set_context(ctx) # CA, cert reqs
client.reconnect_delay_set(min_delay=1, max_delay=30)
def on_connect(c, u, f, rc, props=None):
c.subscribe(DOWN_TOPIC)
# 上线即上报
schedule_report()
def on_disconnect(c, u, rc, props=None):
# 别在这里阻塞重连,paho 内部已启自动重连
log.warning("disconnect rc=%s", rc)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
# 后台线程跑网络循环
client.loop_start()
# 采集线程独立,通过队列交发
from queue import Queue
q = Queue(maxsize=1000)
def collector():
while True:
payload = poll_all() # 非阻塞、带超时、连接池复用
q.put(payload)
time.sleep(POLL_INT)
def publisher():
while True:
payload = q.get()
# 检查连接状态,未连则本地缓存
if client.is_connected():
info = client.publish(UP_TOPIC, json.dumps(payload), qos=1)
info.wait_for_publish(timeout=2) # 别无限阻塞
else:
local_cache(payload)
q.task_done()
# 保活:paho 已按 keepalive=60 发 PINGREQ;额外应用级心跳事件上报
def report_heartbeat():
while True:
client.publish(f"$thing/up/event/{PRODUCT_ID}/{DEVICE_NAME}",
json.dumps({"method":"event_post","params":{"heartbeat":True}}), qos=0)
time.sleep(30)要点:
reconnect_delay_set + loop_start 内置重连退避,勿在回调里自写重连 is_connected(),失败落本地 SQLite,重连后补传(带去重 msg_id/时间戳) ModbusTcpClient 内部复用,别每次 new;失败指数退避,单台故障隔离 arping 或重建客户端;DHCP 租约固定保留 IP,交换机端口安全绑定 MAC # 伪代码级,落地时替换鉴权/物模型
import threading, time, json, paho.mqtt.client as mqtt
RUN = True
def net_loop():
cli.loop_forever(retry_first_connection=True) # 阻塞线程
def worker():
while RUN:
pl = collect() # Modbus TCP, pooled, timeout-guarded
if cli.is_connected():
cli.publish(UP_TOPIC, json.dumps(pl), qos=1)
else:
cache.append(pl)
if cli.is_connected() and cache:
drain = cache[:]; cache.clear()
for old in drain:
cli.publish(UP_TOPIC, json.dumps(old), qos=1)
time.sleep(POLL_INT)
t1 = threading.Thread(target=net_loop, daemon=True); t1.start()
t2 = threading.Thread(target=worker, daemon=True); t2.start()别在 worker 里调 loop_start 又 loop_forever,二选一;多线程共享 cli 需确认 paho 线程安全边界,推荐单循环 + 异步回调。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。