7.6 KiB
7.6 KiB
MQTT 原始数据接收与保存设计(双阶段)
1. 背景与目标
在现有 full-stack-fastapi-template 工程基础上,新增 MQTT 采集能力,用于接收终端上报的 GNSS 数据并可靠保存,支持后续:
- 前端秒级实时展示(设备看板 + 逐条观测流)
- 按天/时间段下载原始数据供外部软件分析
本设计阶段仅实现“接收 + 保存 + 可解析落库链路”,不实现前端页面。
2. 已确认约束
- 部署环境:腾讯云,同机已有 EMQX
- Broker 地址:
101.35.119.226:1883 - 鉴权方式:用户名/密码
- Topic:仅
terminal/{device_id}/raw device_id:直接从 topic 中提取- 上报形态:每 1~10 秒一条消息,每条消息为“完整帧/完整语句集合”
- 负载内容:可能为 NMEA only、RTCM3.2 only 或二者混合
- NMEA 类型:GGA / RMC / GSV / GSA / GST
- RTCM:本期仅做“消息类型级别摘要”
- 存储策略:90 天热数据 + 本地冷归档
- 下载要求:下载内容必须是原始 MQTT payload 字节流,不附加元信息
- 下载文件后缀:
.txt(内容可包含二进制字节)
3. 架构方案与结论
3.1 备选方案
- FastAPI 内嵌 MQTT 客户端
- DB 队列双阶段(独立 ingestor + parser worker)
- 外部消息队列三阶段
3.2 选型结论
采用 方案 2:DB 队列双阶段。
理由:
- 避免 FastAPI 多 worker 下重复订阅/重复入库风险
- 接收链路与解析链路解耦,稳定性更高
- 原始数据先落库,可重试、可回放、可追溯
- 为后续实时展示和下载提供稳定基础
4. 组件职责
4.1 mqtt-ingestor(采集层)
- 常驻独立进程,连接 EMQX 并订阅
terminal/+/raw - 回调中仅执行轻量逻辑:
- 提取
device_id - 记录
topic/qos/retain/received_at - 原样保存
payload_bytes - 计算
payload_sha256与payload_size - 插入
terminal_message_raw(parse_status='pending')
- 提取
- 不执行 NMEA/RTCM 深解析
4.2 parser-worker(解析层)
- 轮询
terminal_message_raw中待解析记录 - 解析 NMEA + RTCM3.2 摘要
- 写入事件表
terminal_observation_event - Upsert 最新状态表
terminal_device_state - 失败可重试,错误可观测,不丢原始数据
4.3 backend API(服务层)
- 本期不新增前端页面
- 后续基于
event + state + archive manifest增加秒级展示与下载接口
5. 数据模型设计
5.1 terminal_message_raw(热数据原始表)
用途:保存原始消息,作为解析队列与下载来源。
建议字段:
id(uuid, pk)device_id(text, index)topic(text)received_at(timestamptz, index)payload_bytes(bytea)payload_size(int)payload_sha256(char(64), index)qos(smallint)retain(bool)parse_status(pending|processing|succeeded|failed, index)parse_attempts(int, default 0)next_retry_at(timestamptz, nullable, index)parse_error(text, nullable)created_at(timestamptz)
5.2 terminal_observation_event(解析事件流)
用途:支撑逐条观测流展示与历史分析。
建议字段:
id(uuid, pk)raw_id(uuid, fk -> terminal_message_raw.id, index)device_id(text, index)event_time(timestamptz, index)event_type(text) 示例:nmea_gga/nmea_rmc/nmea_gsv/nmea_gsa/nmea_gst/rtcm_summarypayload_json(jsonb)created_at(timestamptz)
5.3 terminal_device_state(设备最新状态)
用途:支撑前端设备看板秒级刷新。
建议字段:
device_id(text, pk)last_event_time(timestamptz)fix_status(text)lat/lon(double precision)alt_m(double precision)satellites_used(int)satellite_signal_summary(jsonb)rtcm_type_summary(jsonb)updated_at(timestamptz)
5.4 terminal_raw_archive_manifest(冷归档索引)
用途:记录本地归档文件与时间范围映射。
建议字段:
id(uuid, pk)device_id(text, index)range_start/range_end(timestamptz, index)file_path(text, unique)file_size(bigint)sha256(char(64))record_count(bigint)created_at(timestamptz)
6. 双阶段流水线细节
6.1 入站(ingestor)
- MQTT 建议参数:
clean_session=False、固定client_id、QoS=1 - 批量入库建议:每 100 条或 200ms flush 一次事务
- 目标:尽快持久化原始字节,避免解析阻塞接收
6.2 解析(parser-worker)
批处理取数:
- 条件:
parse_status='pending'且next_retry_at到期 - 顺序:
ORDER BY received_at - 锁:
FOR UPDATE SKIP LOCKED - 批量:
LIMIT 100~500
处理规则:
- 成功:
parse_status='succeeded' - 失败:
parse_status='failed'+parse_attempts+1+parse_error - 重试:退避后设置
next_retry_at
幂等建议:
- 事件表增加防重策略(如
raw_id + event_type + event_time约束或哈希去重) terminal_device_state用 upsert,仅更新更晚event_time
7. 解析策略(本期)
7.1 NMEA
- 解析 GGA/RMC/GSV/GSA/GST
- 每条语句产出一条
terminal_observation_event
7.2 RTCM3.2
- 仅提取消息类型摘要(如 1005/1077/1087)
- 每条 raw payload 产出一条
rtcm_summary事件,包含类型计数与最近出现时间
7.3 混合负载
- 单 payload 中可同时包含 NMEA 与 RTCM 片段
- 解析器按字节扫描拆分,分别产出事件
- 部分片段解析失败不影响其余片段;原始 payload 永远保留
8. 热数据与本地冷归档
8.1 热数据
terminal_message_raw保留 90 天
8.2 本地冷归档
- 定时任务离峰执行
- 归档粒度:
device_id + 日(必要时可按小时分片) - 路径示例:
/data/archive/gnss/<device_id>/YYYY-MM-DD/ - 文件内容:仅原始 payload 字节流拼接(无额外元信息)
- 文件名示例:
device_<id>_<start>_<end>.txt
8.3 校验与清理
- 写入
manifest:时间范围、条数、sha256、文件路径 - 校验通过后,清理超过 90 天热数据
9. 下载行为约束(后续接口实现遵循)
- 下载内容必须与原始 MQTT payload 字节一致
- 近 90 天:从热表按时间顺序拼接输出
- 超过 90 天:从本地归档读取输出
- 跨热/冷边界:按时间顺序拼接两段数据
10. 运维与可观测性
必须监控:
- ingestor:连接状态、入站速率、入库失败
- parser:pending backlog、解析成功率、平均延迟、失败重试
- archive:每日归档成功/失败、磁盘余量
建议告警:
pending队列持续增长- parser 失败率超阈值
- 归档目录磁盘不足
11. 配置项建议(.env)
MQTT_HOST=101.35.119.226MQTT_PORT=1883MQTT_USERNAME=...MQTT_PASSWORD=...MQTT_TOPIC_PATTERN=terminal/+/rawMQTT_CLIENT_ID=spatialhub-mqtt-ingestorRAW_HOT_RETENTION_DAYS=90RAW_ARCHIVE_BASE_DIR=/data/archive/gnssPARSER_BATCH_SIZE=200PARSER_POLL_INTERVAL_MS=300
12. 风险与后续演进
- 使用
.txt扩展名存放二进制流可能被文本工具误打开;但不影响下游软件按字节读取 - 当设备量增长时,可水平扩展 parser-worker(依赖
SKIP LOCKED) - 后续实时能力:新增 SSE/WebSocket 推送层,数据源直接复用
event/state
13. 验收标准(本期)
- 能稳定接收
terminal/{device_id}/raw消息并原样入库 - 混合 payload 可提取 NMEA 事件和 RTCM 类型摘要
- 设备最新状态可持续更新
- 支持 90 天热数据保留与本地冷归档落盘
- 能按时间段导出原始数据(文件后缀
.txt,内容无附加字段)