后端如何订阅 MQTT 并入库
一、整体架构回顾(聚焦后端部分)
LoRa 基站(MQTT 发布者)
↓ MQTT(主题:lorawan/devices/+/up)
MQTT Broker(如 Mosquitto,运行在服务器上)
↓ 转发消息
后端服务(Python 程序)
├─ 订阅 MQTT 主题
├─ 解析 JSON 数据
└─ 存入 MySQL / SQLite 数据库
浏览器通过 HTTP API 查询数据库(这部分在最后简单说明)。
二、准备工作
1. 安装 Python 库
pip install paho-mqtt mysql-connector-python
# 如果只用 SQLite,不需要安装 mysql-connector
2. 准备数据库
选项 A:使用 MySQL(正式环境)
CREATE DATABASE iot_data;
USE iot_data;
CREATE TABLE sensor_data (
id INT AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(50) NOT NULL,
temperature FLOAT,
humidity FLOAT,
rssi INT,
received_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
选项 B:使用 SQLite(轻量级测试)
# 无需安装,Python 自带
sqlite3 iot_data.db
CREATE TABLE sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
temperature REAL,
humidity REAL,
rssi INTEGER,
received_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
三、完整 Python 后端代码
文件名:mqtt_subscriber.py import json import mysql.connector import sqlite3 import paho.mqtt.client as mqtt from datetime import datetime # ========== 配置部分 ========== MQTT_BROKER = "192.168.1.100" # 改成你的 MQTT Broker IP MQTT_PORT = 1883 MQTT_TOPIC = "lorawan/devices/+/up" # + 是通配符,匹配任意设备ID MQTT_CLIENT_ID = "backend_subscriber" # 数据库选择:True=MySQL, False=SQLite USE_MYSQL = False if USE_MYSQL: DB_CONFIG = { "host": "localhost", "user": "root", "password": "yourpassword", "database": "iot_data" } else: SQLITE_DB_PATH = "iot_data.db" # ========== 数据库操作函数 ========== def save_to_mysql(device_id, temp, hum, rssi): """保存数据到 MySQL""" conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() sql = """ INSERT INTO sensor_data (device_id, temperature, humidity, rssi) VALUES (%s, %s, %s, %s) """ cursor.execute(sql, (device_id, temp, hum, rssi)) conn.commit() cursor.close() conn.close() print(f"[MySQL] 已保存: {device_id} @ {temp}°C, {hum}%") def save_to_sqlite(device_id, temp, hum, rssi): """保存数据到 SQLite""" conn = sqlite3.connect(SQLITE_DB_PATH) cursor = conn.cursor() sql = """ INSERT INTO sensor_data (device_id, temperature, humidity, rssi) VALUES (?, ?, ?, ?) """ cursor.execute(sql, (device_id, temp, hum, rssi)) conn.commit() conn.close() print(f"[SQLite] 已保存: {device_id} @ {temp}°C, {hum}%") # ========== MQTT 回调函数 ========== def on_connect(client, userdata, flags, rc): """连接 MQTT Broker 成功时调用""" if rc == 0: print(f"✅ 成功连接到 MQTT Broker: {MQTT_BROKER}") # 订阅主题 client.subscribe(MQTT_TOPIC) print(f"📡 已订阅主题: {MQTT_TOPIC}") else: print(f"❌ 连接失败,返回码: {rc}") def on_message(client, userdata, msg): """收到 MQTT 消息时调用""" try: # 1. 解析 JSON payload = json.loads(msg.payload.decode()) print(f"📨 收到消息: 主题={msg.topic}, 数据={payload}") # 2. 提取字段(根据你的实际数据格式调整) # 示例数据格式1: {"device":"A001","temp":25.3,"hum":60,"rssi":-75} # 示例数据格式2: {"end_device_ids":{"device_id":"A001"},"uplink_message":{"decoded_payload":{"temperature":25.3,"humidity":60},"rx_metadata":[{"rssi":-75}]}} # 这里按简单格式演示 device_id = payload.get("device") or payload.get("end_device_ids", {}).get("device_id") temperature = payload.get("temp") or payload.get("uplink_message", {}).get("decoded_payload", {}).get("temperature") humidity = payload.get("hum") or payload.get("uplink_message", {}).get("decoded_payload", {}).get("humidity") rssi = payload.get("rssi") or (payload.get("uplink_message", {}).get("rx_metadata", [{}])[0].get("rssi") if payload.get("uplink_message") else None) # 3. 检查必要字段 if device_id is None or temperature is None: print("⚠️ 数据格式不完整,跳过") return # 4. 存入数据库 if USE_MYSQL: save_to_mysql(device_id, temperature, humidity, rssi) else: save_to_sqlite(device_id, temperature, humidity, rssi) except json.JSONDecodeError: print(f"❌ JSON 解析失败: {msg.payload}") except Exception as e: print(f"❌ 处理消息时出错: {e}") # ========== 主程序 ========== def main(): # 创建 MQTT 客户端 client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=MQTT_CLIENT_ID) # 绑定回调函数 client.on_connect = on_connect client.on_message = on_message # 连接到 Broker(如果需要用户名密码,取消下面注释) # client.username_pw_set("username", "password") client.connect(MQTT_BROKER, MQTT_PORT, 60) # 开始循环(阻塞式) print("🚀 后端服务已启动,等待 MQTT 消息...") client.loop_forever() if __name__ == "__main__": main()
四、运行后端服务
python mqtt_subscriber.py
预期输出:
🚀 后端服务已启动,等待 MQTT 消息...
✅ 成功连接到 MQTT Broker: 192.168.1.100
📡 已订阅主题: lorawan/devices/+/up
📨 收到消息: 主题=lorawan/devices/A001/up, 数据={'device': 'A001', 'temp': 25.3, 'hum': 60, 'rssi': -75}
[MySQL] 已保存: A001 @ 25.3°C, 60%
五、与你的 AJAX 后端结合(补充 HTTP API)
你还需要一个 HTTP 服务供浏览器 AJAX 调用。可以用 Flask 在同一个 Python 进程中运行(多线程)。
安装 Flask
pip install flask
修改代码,加入 HTTP API
from flask import Flask, jsonify import sqlite3 # 或 mysql.connector app = Flask(__name__) @app.route('/api/latest') def get_latest(): """返回最新一条数据""" conn = sqlite3.connect(SQLITE_DB_PATH) cursor = conn.cursor() cursor.execute("SELECT device_id, temperature, humidity, received_at FROM sensor_data ORDER BY received_at DESC LIMIT 1") row = cursor.fetchone() conn.close() if row: return jsonify({ "device_id": row[0], "temperature": row[1], "humidity": row[2], "time": row[3] }) else: return jsonify({"error": "暂无数据"}), 404 # 在主函数中启动 Flask(需要多线程) from threading import Thread def start_flask(): app.run(host='0.0.0.0', port=8080) def main(): # 启动 Flask 线程 Thread(target=start_flask, daemon=True).start() # 原来的 MQTT 循环 client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) # ... 后面跟之前一样
六、测试整个流程
-
启动 MQTT Broker(如果还没有)
mosquitto -v -
启动后端服务
python mqtt_subscriber.py -
模拟 LoRa 基站发布消息(测试用)
mosquitto_pub -h 192.168.1.100 -t "lorawan/devices/A001/up" -m '{"device":"A001","temp":25.3,"hum":60,"rssi":-75}' -
浏览器 AJAX 查询
-
打开浏览器访问:
http://服务器IP:8080/api/latest -
返回 JSON 数据
-
七、关键点总结
| 步骤 | 说明 |
|---|---|
| MQTT 订阅 | 用 client.subscribe("lorawan/devices/+/up") 订阅通配符主题 |
| 消息处理 | 在 on_message 回调中解析 JSON |
| 数据入库 | 调用数据库插入函数,支持 MySQL 或 SQLite |
| 并发处理 | 如果需要同时提供 HTTP API,用多线程(Flask 在另一个线程) |
| 错误处理 | 捕获 JSON 解析异常、数据库异常,防止程序崩溃 |
八、实际部署注意事项
-
QoS 设置:如果担心丢消息,订阅时设置
-
断线重连:
paho-mqtt默认会自动重连,但可以加on_disconnect回调 -
性能优化:高并发时,数据库写入用连接池(如
SQLAlchemy) -
安全性:
-
MQTT 加用户名密码
-
用 TLS(端口 8883)
-
数据库密码用环境变量,不要写死在代码里
-

浙公网安备 33010602011771号