MQTT的消息订阅机制

我来详细解释 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)
            }
        }
    }
}

完整的数据流程说明

  1. 连接建立流程
程序启动
读取配置
创建MQTT客户端
连接MQTT服务器
订阅主题
  1. 消息处理流程
MQTT消息到达
消息处理函数
解析JSON数据
发送到数据通道
定时器收集数据
批量处理数据
写入数据库/发送HTTP请求

关键概念解释

  1. MQTT 客户端选项
opts := MQTT.NewClientOptions()
opts.SetClientID("unique_client_id")    // 客户端唯一标识
opts.SetResumeSubs(true)                // 断线重连后恢复订阅
opts.CleanSession = false               // 保留会话状态
opts.SetAutoReconnect(true)             // 自动重连
  1. 消息处理机制
  • messageHandler 函数在收到消息时自动调用
  • 使用通道(channel)进行异步处理
  • 批量处理减少数据库操作频率
  1. 数据流控制
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"
        }]
    }
}

调试建议

  1. 日志监控
// 添加关键点日志
fmt.Printf("收到传感器 %s 的数据: %v\n", 
    inputData.Data.ResultData[0].SensorID,
    inputData.Data.ResultData[0].Value)
  1. 错误处理
if err != nil {
    
    
    fmt.Printf("处理数据时发生错误: %s\n", err)
    // 可以添加重试逻辑或告警机制
    return
}
  1. 性能监控
start := time.Now()
processDataBatch(batch)
fmt.Printf("处理 %d 条数据耗时: %v\n", 
    len(batch), time.Since(start))

这样的设计确保了:

  • 消息可靠接收
  • 数据异步处理
  • 批量操作提高效率
  • 错误可追踪和处理
  • 系统可扩展性

猜你喜欢

转载自blog.csdn.net/u011027104/article/details/143505409