我来详细解释 MQTT 订阅的工作流程和代码实现:
1. MQTT 订阅基本概念
// 1. 首先建立 MQTT 连接
func connectMQTT() {
mqttURI := viper.GetString("mqttURI")
opts := MQTT.NewClientOptions().AddBroker(mqttURI)
opts.SetClientID("go_mqtt_client0000187")
opts.SetResumeSubs(true) // 断线重连时恢复订阅
opts.CleanSession = false // 保留会话
opts.SetAutoReconnect(true) // 自动重连
mqttClient = MQTT.NewClient(opts)
if token := mqttClient.Connect(); token.Wait() && token.Error() != nil {
fmt.Printf("连接MQTT服务器失败: %s\n", token.Error())
panic(token.Error())
}
fmt.Println("MQTT已连接")
}
2. 订阅主题和消息处理
func subscribeAndProcessBatch(topic string, batchSize int) {
// 定义消息处理函数
messageHandler := func(client MQTT.Client, msg MQTT.Message) {
fmt.Printf("收到消息: Topic=%s, Payload=%s\n",
msg.Topic(), string(msg.Payload()))
var inputData InputData
err := json.Unmarshal(msg.Payload(), &inputData)
if err != nil {
fmt.Printf("解析消息失败: %s\n", err)
return
}
// 将数据发送到通道
dataChannel <- inputData
}
// 订阅主题
token := mqttClient.Subscribe(topic, 0, messageHandler)
token.Wait()
if token.Error() != nil {
fmt.Printf("订阅失败: %s\n", token.Error())
return
}
}
3. 数据流处理
// 数据通道定义
var dataChannel = make(chan InputData, 100) // 缓冲区大小为100
// 定时处理数据
func processDataWithTimer() {
ticker := time.NewTicker(timeSleep)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// 收集数据
var batch []InputData
collecting := true
for collecting {
select {
case data := <-dataChannel:
batch = append(batch, data)
default:
collecting = false
}
}
// 处理收集到的数据
if len(batch) > 0 {
processDataBatch(batch)
}
}
}
}
完整的数据流程说明
- 连接建立流程:
- 消息处理流程:
关键概念解释
- MQTT 客户端选项:
opts := MQTT.NewClientOptions()
opts.SetClientID("unique_client_id") // 客户端唯一标识
opts.SetResumeSubs(true) // 断线重连后恢复订阅
opts.CleanSession = false // 保留会话状态
opts.SetAutoReconnect(true) // 自动重连
- 消息处理机制:
messageHandler
函数在收到消息时自动调用- 使用通道(channel)进行异步处理
- 批量处理减少数据库操作频率
- 数据流控制:
select {
case data := <-dataChannel: // 从通道读取数据
batch = append(batch, data)
default: // 通道为空时退出循环
collecting = false
}
实际应用示例
假设监控土压力传感器:
// 配置文件示例 config016.yaml
mqttTopic: "sensors/earth_pressure/"
time_interval: "30s"
// 消息格式示例
{
"data": {
"resultData": [{
"sensorID": "EP001",
"value": 123.45,
"timestamp": "2024-01-20T10:00:00Z"
}]
}
}
调试建议
- 日志监控:
// 添加关键点日志
fmt.Printf("收到传感器 %s 的数据: %v\n",
inputData.Data.ResultData[0].SensorID,
inputData.Data.ResultData[0].Value)
- 错误处理:
if err != nil {
fmt.Printf("处理数据时发生错误: %s\n", err)
// 可以添加重试逻辑或告警机制
return
}
- 性能监控:
start := time.Now()
processDataBatch(batch)
fmt.Printf("处理 %d 条数据耗时: %v\n",
len(batch), time.Since(start))
这样的设计确保了:
- 消息可靠接收
- 数据异步处理
- 批量操作提高效率
- 错误可追踪和处理
- 系统可扩展性