UPS数据采集时mosquitto_pub运行一段时间返回4的故障及重试方案咨询
故障原因分析
- 核心原因是
mosquitto_pub的长连接会话失效:你当前在awk中调用mosquitto_pub -l时,awk会在第一次匹配到规则时启动该进程并保持长管道连接,mosquitto_pub仅在启动时向MQTT broker发起一次鉴权,broker默认会对长时间无活动/超过会话有效期的连接自动清理鉴权状态,后续再推送数据时broker就会返回鉴权失败的错误码4。 - 次要可能原因:TCP长连接意外中断后
mosquitto_pub默认不会自动重连,重连时如果出现DNS解析异常、broker临时不可达等情况,也可能抛出类似错误,重启整条命令时会重新建立连接完成鉴权,因此可以恢复。
自动重试方案
以下两种优化方案均可实现故障自动恢复:
方案1:调整调用结构,用shell循环统一管理(推荐)
不要在awk内部调用MQTT推送进程,改为将awk解析后的结果直接输出到标准输出,整条管道的异常都可以被shell捕获并重试:
- 先修改
parseUPS.awk的核心逻辑,去掉内部调用mosquitto的逻辑,改为直接输出解析后的JSON,同时强制刷新缓冲区避免数据堆积:
/Turn Off/ { out=sprintf(....); print out; fflush(); }
- 编写shell循环执行整条链路,异常时自动重启:
#!/bin/bash while true; do # 执行采集解析推送全链路 ups_status | awk -f parseUPS.awk | mosquitto_pub -h mqtt.local.net -t ups -l # 输出错误日志,可根据PIPESTATUS定位具体哪个环节退出 echo "$(date "+%Y-%m-%d %H:%M:%S") 进程异常退出,各环节退出码:ups_status=${PIPESTATUS[0]} awk=${PIPESTATUS[1]} mosquitto_pub=${PIPESTATUS[2]}" >> /var/log/ups_monitor.log # 重试前等待10秒,避免broker被频繁重连触发限流 sleep 10 done
方案2:awk内捕获管道错误直接退出
如果你不想修改原有调用结构,可以在awk内每次发送后关闭管道、检查错误,异常时直接退出awk进程,触发上层shell循环重启:
修改parseUPS.awk的核心逻辑:
/Turn Off/ { out=sprintf(....); mqtt_cmd = "mosquitto_pub -h mqtt.local.net -t ups -l" print out | mqtt_cmd # 关闭当前管道,下次发送会重新启动进程完成鉴权,避免长连接超时 close(mqtt_cmd) # 检查管道执行错误 if (ERRNO != "") { print "MQTT推送失败:" ERRNO > "/dev/stderr" # 退出awk进程,触发上层shell循环重启 exit 1 } }
上层shell循环可以用简化版本:
while true; do ups_status | awk -f parseUPS.awk echo "$(date) 进程异常退出,10秒后重试" >> /var/log/ups_monitor.log sleep 10 done
这个方案的优势是每次推送都会重新发起MQTT鉴权,从根源上避免长连接会话超时的问题,2秒一次的推送频率下额外的连接开销完全可以忽略。
内容的提问来源于stack exchange,提问作者Leif Neland
相关产品推荐
相关产品推荐

