摘要

本文深入讲解DolphinDB MQTT协议接入技术。从MQTT协议原理到插件配置,从主题订阅到数据解析,从实时写入到高可用部署,全面介绍MQTT数据采集的核心方法。通过丰富的代码示例,帮助读者掌握工业设备数据采集的核心技能。


一、MQTT协议概述

1.1 什么是MQTT

MQTT(Message Queuing Telemetry Transport)是轻量级消息传输协议:

MQTT架构

设备/传感器

MQTT Broker

设备/传感器

设备/传感器

订阅者1

订阅者2

DolphinDB

1.2 MQTT特点

特点 说明
轻量级 协议简单,开销小
发布订阅 一对多通信
QoS级别 消息质量保证
持久会话 断线重连

1.3 MQTT在工业物联网中的应用

场景 说明
设备数据采集 传感器数据上报
远程控制 设备指令下发
状态监控 设备状态推送
告警通知 异常告警推送

二、DolphinDB MQTT插件

2.1 插件安装

// 检查插件是否已加载
getLoadedPlugins()

// 加载MQTT插件
loadPlugin("mqtt")

// 查看插件函数
mqtt::getPluginFunctions()

2.2 插件配置

// MQTT连接配置
config = dict(STRING, ANY, [
    ["host", "localhost"],
    ["port", 1883],
    ["clientId", "dolphindb_client"],
    ["username", "admin"],
    ["password", "password"],
    ["keepAlive", 60],
    ["cleanSession", true]
])

三、MQTT连接管理

3.1 创建连接

// 创建MQTT连接
conn = mqtt::connect("localhost", 1883, "dolphindb_client")

// 查看连接状态
mqtt::getStatus(conn)

// 断开连接
mqtt::disconnect(conn)

3.2 连接参数

// 完整连接参数
conn = mqtt::connect(
    "localhost",           // broker地址
    1883,                  // 端口
    "dolphindb_client",    // 客户端ID
    "admin",               // 用户名
    "password",            // 密码
    60,                    // keepAlive时间(秒)
    true                   // cleanSession
)

3.3 连接状态监控

// 监控连接状态
def monitorConnection(conn) {
    status = mqtt::getStatus(conn)
    if (status == "connected") {
        print("MQTT连接正常")
    } else {
        print("MQTT连接异常: " + status)
    }
}

monitorConnection(conn)

四、主题订阅

4.1 订阅主题

// 创建流表接收数据
share streamTable(1:0, 
    `device_id`timestamp`temperature`humidity,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE]) as mqtt_stream

// 订阅MQTT主题
mqtt::subscribe(conn, "sensor/data", mqtt_stream)

// 订阅多个主题
mqtt::subscribe(conn, "sensor/temperature", temp_stream)
mqtt::subscribe(conn, "sensor/humidity", humid_stream)

// 订阅通配符主题
mqtt::subscribe(conn, "sensor/+/data", mqtt_stream)  // + 单级通配符
mqtt::subscribe(conn, "sensor/#", mqtt_stream)        // # 多级通配符

4.2 取消订阅

// 取消订阅
mqtt::unsubscribe(conn, "sensor/data")

// 取消所有订阅
mqtt::unsubscribeAll(conn)

4.3 QoS级别

// QoS级别说明
// QoS 0: 最多一次,可能丢失
// QoS 1: 至少一次,可能重复
// QoS 2: 恰好一次,保证送达

// 订阅时指定QoS
mqtt::subscribe(conn, "sensor/data", mqtt_stream, 1)  // QoS 1

五、数据解析

5.1 JSON数据解析

// 创建JSON解析流表
share streamTable(1:0, 
    `device_id`timestamp`temperature`humidity,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE]) as json_stream

// JSON解析函数
def parseJsonData(jsonStr) {
    data = parseJson(jsonStr)
    return table(
        data.device_id as device_id,
        data.timestamp as timestamp,
        data.temperature as temperature,
        data.humidity as humidity
    )
}

// 订阅并解析JSON
mqtt::subscribe(conn, "sensor/json", json_stream, 
    def(msg) {
        parseJsonData(msg)
    })

5.2 自定义格式解析

// 自定义格式:逗号分隔
// 格式:device_id,timestamp,temperature,humidity

def parseCsvData(csvStr) {
    parts = split(csvStr, ",")
    return table(
        parts[0] as device_id,
        timestamp(parts[1]) as timestamp,
        double(parts[2]) as temperature,
        double(parts[3]) as humidity
    )
}

// 订阅并解析
mqtt::subscribe(conn, "sensor/csv", csv_stream, parseCsvData)

5.3 二进制数据解析

// 二进制数据解析
def parseBinaryData(binaryData) {
    // 假设二进制格式:
    //4字节:device_id (int32)
    //8字节:temperature (double)
    //8字节:humidity (double)
    
    deviceId = parseInt32(binaryData, 0)
    temperature = parseDouble(binaryData, 4)
    humidity = parseDouble(binaryData, 12)
    
    return table(
        deviceId as device_id,
        now() as timestamp,
        temperature as temperature,
        humidity as humidity
    )
}

六、数据写入

6.1 实时写入流表

// 创建流表
share streamTable(100000:0, 
    `device_id`timestamp`temperature`humidity,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE]) as sensor_stream

// 启用持久化
enableTablePersistence(sensor_stream, true, true, 1000000)

// 订阅MQTT写入流表
mqtt::subscribe(conn, "sensor/data", sensor_stream)

6.2 写入分布式表

// 创建分布式表
db = database("dfs://mqtt_db", VALUE, 1..1000)
schema = table(1:0, `device_id`timestamp`temperature`humidity,
               [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE])
db.createPartitionedTable(schema, `sensor_data, `device_id)

// 创建流表
share streamTable(100000:0, 
    `device_id`timestamp`temperature`humidity,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE]) as mqtt_stream

// 订阅流表写入分布式表
subscribeTable(, "mqtt_stream", "persist", -1,
    def(msg) {
        loadTable("dfs://mqtt_db", "sensor_data").append!(msg)
    }, 10000, 5000)

// 订阅MQTT
mqtt::subscribe(conn, "sensor/data", mqtt_stream)

6.3 批量写入优化

// 批量写入配置
// batchSize: 批量大小
// throttle: 节流时间(毫秒)

subscribeTable(, "mqtt_stream", "batch_persist", -1,
    def(msg) {
        loadTable("dfs://mqtt_db", "sensor_data").append!(msg)
    }, 
    10000,   // batchSize: 100005000)    // throttle: 5

七、高可用部署

7.1 断线重连

// 断线重连配置
def connectWithRetry(host, port, clientId, maxRetries = 5) {
    retries = 0
    while (retries < maxRetries) {
        try {
            conn = mqtt::connect(host, port, clientId)
            print("MQTT连接成功")
            return conn
        } catch (ex) {
            retries += 1
            print("连接失败,重试 " + string(retries) + "/" + string(maxRetries))
            sleep(5000)  // 等待5秒重试
        }
    }
    throw "MQTT连接失败"
}

// 使用
conn = connectWithRetry("localhost", 1883, "dolphindb_client")

7.2 心跳检测

// 心跳检测函数
def heartbeat(conn, interval = 30) {
    while (true) {
        status = mqtt::getStatus(conn)
        if (status != "connected") {
            print("连接断开,尝试重连...")
            // 重连逻辑
        }
        sleep(interval * 1000)
    }
}

// 启动心跳(异步)
submitJob("mqtt_heartbeat", "MQTT心跳", def() { heartbeat(conn) })

7.3 数据缓冲

// 数据缓冲:断线时缓存数据
share table(100000:0,
    `device_id`timestamp`temperature`humidity,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE]) as buffer_table

// 写入时先写缓冲
def writeToBuffer(data) {
    buffer_table.append!(data)
}

// 定时从缓冲写入分布式表
def flushBuffer() {
    data = select * from buffer_table
    if (data.rows() > 0) {
        loadTable("dfs://mqtt_db", "sensor_data").append!(data)
        truncate(buffer_table)
    }
}

// 定时任务
scheduleJob("flush_buffer", "刷新缓冲", flushBuffer,
            00:01, 2024.01.01, 2030.12.31, 'D')

八、实战案例

8.1 工业设备数据采集系统

// ========== 工业设备数据采集系统 ==========

// 1. 创建分布式表
db = database("dfs://iot_mqtt_db", VALUE, 1..1000)
schema = table(1:0, 
    `device_id`timestamp`temperature`humidity`pressure`vibration,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, DOUBLE])
db.createPartitionedTable(schema, `device_data, `device_id)

// 2. 创建流表
share streamTable(100000:0, 
    `device_id`timestamp`temperature`humidity`pressure`vibration,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, DOUBLE]) as mqtt_stream

// 3. 启用持久化
enableTablePersistence(mqtt_stream, true, true, 1000000)

// 4. 订阅流表写入分布式表
subscribeTable(, "mqtt_stream", "persist", -1,
    def(msg) {
        loadTable("dfs://iot_mqtt_db", "device_data").append!(msg)
    }, 10000, 5000)

// 5. 连接MQTT
conn = mqtt::connect("mqtt-broker.local", 1883, "dolphindb_iot")

// 6. 订阅主题
mqtt::subscribe(conn, "factory/+/sensor", mqtt_stream)

// 7. 监控函数
def monitorMqtt() {
    print("=== MQTT监控 ===")
    print("连接状态: " + mqtt::getStatus(conn))
    print("流表行数: " + string(exec count(*) from mqtt_stream))
    
    t = loadTable("dfs://iot_mqtt_db", "device_data")
    print("分布式表行数: " + string(exec count(*) from t))
}

monitorMqtt()

print("工业设备数据采集系统启动完成")

九、总结

本文详细介绍了DolphinDB MQTT协议接入:

  1. MQTT协议:轻量级、发布订阅、QoS级别
  2. 插件配置:安装、加载、配置
  3. 连接管理:创建连接、状态监控
  4. 主题订阅:订阅主题、QoS配置
  5. 数据解析:JSON解析、自定义格式、二进制解析
  6. 数据写入:流表写入、分布式表写入、批量优化
  7. 高可用:断线重连、心跳检测、数据缓冲

思考题

  1. MQTT协议有什么优势?
  2. 如何设计高可用的MQTT数据采集系统?
  3. 如何处理MQTT消息丢失问题?

参考资料


Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐