realtime-device-gateway-architecture.md 17 KB

实时设备数据网关最终方案

本文档用于收敛当前关于 GPS 模拟、中转、监控、规则判定、回放、通知分发等讨论,给出一版可直接进入实现设计的最终方案。


1. 目标与定位

本系统不再定义为“GPS 模拟器”,而定义为:

  • 一个独立部署的实时设备数据网关
  • 负责实时接入、标准化、路由、订阅、最新状态同步
  • 默认不依赖数据库,不承担历史存储职责
  • 通过插件扩展规则判定、通知分发、回放、归档等能力

一句话定义:

一个以实时中转为核心、以插件扩展业务能力的轻量遥测网关。


2. 设计原则

2.1 核心优先级

核心目标按优先级排序如下:

  1. 实时性能
  2. 稳定性
  3. 低耦合
  4. 易扩展
  5. 易观测

2.2 核心边界

核心服务只负责:

  • 长连接接入
  • 消息标准化
  • 路由转发
  • 订阅管理
  • latest state 缓存
  • 连接管理
  • 心跳和断线清理
  • 基础鉴权
  • 限流与基本防护

核心服务不负责:

  • 历史存储
  • 业务报表
  • 复杂规则引擎
  • 第三方通知发送
  • 业务数据库查询
  • 面向家长端或场控端的专属业务逻辑

3. 总体架构

3.1 拓扑关系

Producer
  ├─ 手机客户端
  ├─ 外部模拟器
  ├─ 回放器
  └─ 设备接入器
          |
          v
Realtime Device Gateway
  ├─ Connection Manager
  ├─ Protocol Layer
  ├─ Session Manager
  ├─ Router / Fanout
  ├─ Latest State Cache
  └─ Plugin Bus
          |
          +--> Consumer
          |     ├─ 小程序 / App
          |     ├─ 家长端
          |     ├─ 场控端
          |     └─ 调试端 / 大屏
          |
          +--> Plugins
                ├─ Rule Engine
                ├─ Dispatcher
                ├─ Recorder
                ├─ Replayer Adapter
                └─ Webhook / Bridge

Business Server
  ├─ 用户与设备关系
  ├─ 配置管理
  ├─ 历史归档
  └─ 业务接口

3.2 在整个系统中的定位

实时网关在整个系统中,不是主业务服务器的替代品,而是一层独立的实时中枢。

职责分工建议固定为:

  • 设备、模拟器、回放器
    • 负责产出实时数据
  • 实时网关
    • 负责接入、路由、订阅、latest state、实时分发、运行态观察
  • 业务服务器
    • 负责用户、设备归属、配置、历史存档、业务查询
  • 插件
    • 负责规则、通知、归档、回放等异步能力

一句话理解:

主业务服务器负责“谁是谁”,实时网关负责“现在发生了什么,发给谁看”。

3.3 服务角色

  • Producer

    • 实时数据生产者
    • 包括真实设备、模拟器、回放器
  • Consumer

    • 实时数据消费者
    • 包括家长端、场控端、调试端、大屏
  • Controller

    • 可向 Producer 或 Gateway 下发控制命令
    • 包括调试控制台、场控后台、回放控制器

3.4 角色使用流程

开发模拟

  1. Controller 在管理台创建 channel
  2. 网关返回 channelId / producerToken / consumerToken
  3. 老模拟器作为 Producer 加入 channel 并开始发 telemetry.location / telemetry.heart_rate
  4. 管理台和调试 Consumer 实时观察数据
  5. 业务服务器此时可以不参与,或仅做低频归档

家长端监控

  1. 真机设备作为 Producer 向网关持续上报位置和状态
  2. 家长端作为 Consumer 订阅某个 deviceIdgroupId
  3. 网关负责实时分发
  4. 业务服务器负责账号关系、历史数据和业务页面

场控

  1. 场控端作为 Consumer 订阅多个设备或一个分组
  2. 网关持续推送实时位置、状态和事件
  3. 如果有控制需求,场控端再以 Controller 身份下发命令
  4. 规则插件可基于同一条实时流做违规判定

归档

  1. Producer 只向网关上报实时数据
  2. Recorder 插件异步消费网关流
  3. Recorder 再按 10-30s 批量写入业务服务器

这样可以保证:

  • 实时链路和归档链路解耦
  • 客户端可以逐步从双写演进到单写网关
  • 实时数据和历史数据口径更一致

4. 部署原则

4.1 独立部署

实时网关必须独立于主业务服务器部署。

这样做的原因:

  • 不占用主业务服务器的实时长连接资源
  • 不让实时 fanout 干扰主业务接口
  • 实时服务可独立扩容
  • 便于分离故障和性能问题

4.2 与主业务服务的关系

主业务服务只提供控制面能力:

  • 用户与设备归属关系
  • 权限配置
  • 策略配置
  • 历史存档入口

实时网关处理数据面:

  • 高频 telemetry
  • 最新状态同步
  • 实时下行通知和控制

原则上,不允许每条实时上报都回主业务服务查库。


5. 0 数据库方案

5.1 可行性

第一版核心网关可以做到 0 数据库依赖。

这里的含义是:

  • 不接 MySQL
  • 不接 PostgreSQL
  • 不接业务持久化存储
  • 不存历史轨迹
  • 不做持久化 session

5.2 保留的内存态

即使 0 数据库,网关仍然必须保留以下运行时内存数据:

  • 连接表
  • 订阅关系
  • 设备 latest state
  • 会话元数据
  • 限流计数
  • 心跳状态

5.3 重启语义

网关重启后允许丢失:

  • 当前连接
  • 当前订阅
  • latest state
  • 临时限流计数

系统恢复方式:

  • 客户端自动重连
  • 重新鉴权
  • 重新订阅
  • Producer 继续上报最新点

这对实时网关是可接受的。


6. 性能目标

6.1 第一版容量目标

第一版至少满足:

  • 1000-2000 设备同时在线上报
  • 默认 1 Hz 实时上报能力
  • 支持部分调试设备升频到 2-5 Hz
  • 端到端实时链路延迟目标 < 500 ms
  • 插件故障不阻塞核心中转

6.2 带宽认知

实时中转一定消耗网关所在服务器的带宽,但不会占用主业务服务器带宽。

不做历史存储,只能减少:

  • 磁盘 IO
  • 存储成本
  • 数据库压力

不能减少:

  • 长连接压力
  • 实时 fanout 压力
  • 网关入口和出口带宽

6.3 核心优化原则

  • 不允许全量广播
  • 必须按设备或组定向订阅
  • payload 尽量小
  • latest state 只保留当前值
  • 插件必须异步消费
  • 高频调试源必须可限流

7. 消息模型

7.1 统一信封

所有进入网关的数据统一使用一个标准信封:

{
  "schemaVersion": 1,
  "messageId": "uuid",
  "timestamp": 1711267200000,
  "topic": "telemetry.location",
  "source": {
    "kind": "producer",
    "id": "watch-001",
    "mode": "real"
  },
  "target": {
    "deviceId": "child-001",
    "groupId": "class-a"
  },
  "payload": {}
}

7.2 topic 分类

建议只保留四类顶级 topic:

  • telemetry.*
  • state.*
  • event.*
  • command.*

示例:

  • telemetry.location
  • telemetry.heart_rate
  • telemetry.motion
  • state.device
  • event.rule_violation
  • event.sos
  • command.replay.start
  • command.simulation.stop

7.3 source mode

建议显式区分数据来源模式:

  • real
  • mock
  • replay
  • control

这样业务侧可以明确识别是真实数据还是模拟/回放数据。


8. 主要数据载荷

8.1 位置 telemetry

{
  "lat": 31.2304,
  "lng": 121.4737,
  "altitude": 12.3,
  "speed": 1.5,
  "bearing": 90,
  "accuracy": 8,
  "coordSystem": "GCJ02"
}

最少保留字段:

  • timestamp
  • lat
  • lng
  • speed
  • bearing
  • accuracy
  • coordSystem

8.2 心率 telemetry

{
  "bpm": 142,
  "confidence": 0.92
}

8.3 运动 telemetry

{
  "cadence": 168,
  "stepCount": 3201,
  "calories": 248.5,
  "movementState": "run"
}

8.4 设备状态

{
  "online": true,
  "battery": 76,
  "network": "4g",
  "charging": false
}

8.5 事件

{
  "eventType": "rule_violation",
  "ruleId": "geo-fence-01",
  "severity": "high",
  "text": "leave allowed area"
}

8.6 控制命令

{
  "command": "simulation.start",
  "args": {
    "sessionId": "sim-001",
    "speedRate": 2
  }
}

9. 订阅与路由模型

9.1 路由单位

实时网关按以下维度路由:

  • deviceId
  • groupId
  • topic

9.2 推荐订阅粒度

  • 订阅某个设备全部实时消息
  • 订阅某个设备某类消息
  • 订阅某个组某类消息

示例:

  • device:child-001
  • device:child-001/topic:telemetry.location
  • group:class-a/topic:event.*

9.3 latest state

每个设备保留一个最新状态快照,不存历史。

用途:

  • 新订阅者刚连上时立即获得当前状态
  • 页面重连后快速恢复
  • 插件可从最新态快速计算

10. 鉴权策略

10.1 不建议完全不鉴权

如果完全不鉴权,会带来两个高风险问题:

  • 任意客户端都能伪造位置或心率
  • 任意客户端都可能下发控制命令

10.2 推荐分级鉴权

  • Producer

    • 必须强鉴权
    • 防止伪造上报
  • Controller

    • 必须强鉴权
    • 防止误操作和恶意控制
  • Consumer

    • 可轻鉴权
    • 可按内网、白名单、临时 token、短期票据放宽

10.3 第一版推荐方式

第一版建议:

  • Producer 使用签名 token 或短期接入 token
  • Controller 使用管理 token
  • Consumer 使用轻量订阅 token

网关本身不查数据库,只做:

  • token 基本校验
  • 角色识别
  • 权限范围校验

复杂鉴权可以由独立鉴权服务承担。


11. 插件体系

11.1 原则

事件处理不是核心同步主链路的一部分,而是插件。

同步主链路只做:

ingest -> normalize -> route -> fanout -> update latest state

然后异步投递到插件总线:

publish -> plugin bus -> plugin async consume

11.2 第一批插件类型

  • Rule Engine
    • 实时规则判定
  • Dispatcher
    • 通知和下行分发
  • Recorder
    • 归档与历史写入
  • Replayer
    • 文件或历史流回放
  • Webhook
    • 向外部系统桥接

11.3 插件要求

  • 不能阻塞主链路
  • 失败必须隔离
  • 可独立启停
  • 最好支持单独部署

12. 规则判定与通知分发

12.1 规则不进核心

规则判定必须在插件层执行,不进入中转核心。

原因:

  • 规则计算不稳定
  • 容易引入复杂状态
  • 容易放大 CPU 和 IO 消耗
  • 不应影响实时转发

12.2 推荐规则类型

  • 阈值规则

    • 心率过高
    • 速度异常
    • 电量过低
  • 时空规则

    • 离开围栏
    • 进入禁区
    • 停留超时
    • 偏离路线
  • 行为规则

    • 长时间静止
    • 轨迹跳变
    • 设备离线

12.3 动作分发

规则命中后由 Dispatcher 插件执行动作:

  • 下发终端警告
  • 推送给家长端
  • 推送给场控端
  • 上报业务服务
  • 触发 Webhook

12.4 规则系统必须处理的问题

  • 去重
  • 冷却时间
  • 恢复事件
  • 幂等

13. 客户端上报策略

13.1 当前推荐的第一阶段方案

客户端保留两条链路:

  • 实时链路
    • 有需要时实时上报到网关
  • 归档链路
    • 10-30s 打包一次发给业务服务器存档

这个方案的优点:

  • 改造快
  • 实时与归档解耦
  • 主业务服务器不承受高频位置流

13.2 双写风险

客户端双写存在天然风险:

  • 两边成功率不同
  • 数据口径可能不一致
  • 客户端逻辑更复杂
  • 重试和去重更难

13.3 推荐演进方向

推荐第二阶段改成:

  • 客户端只向网关实时上报
  • Recorder 插件按 10-30s 批量归档到业务服务器

这样可以保证:

  • 实时和归档数据同源
  • 客户端逻辑更轻
  • 后续回放、规则、告警直接复用同一条实时流

13.4 客户端升频策略

建议客户端支持策略切换:

  • normal
    • 低频或关闭实时上报
  • monitor
    • 1-5s 实时上报
  • debug
    • 1 Hz
  • alert
    • 临时升频

14. 模拟与回放

14.1 模拟器定位

模拟器不再是一个独立特殊系统,而是一个标准 Producer。

它可以上报:

  • telemetry.location
  • telemetry.heart_rate
  • telemetry.motion
  • state.device

14.2 回放器定位

回放器也是 Producer。

区别只是:

  • source mode 为 replay
  • 输入来源是文件或历史流
  • 支持播放、暂停、倍速、循环

14.3 地图模拟器要求

地图拖点时不应直接瞬移发点,至少要支持:

  • 时间戳
  • 插值
  • 平滑
  • 速度和朝向生成

否则业务侧会看到不真实的轨迹。


15. 技术选型建议

15.1 技术选型原则

本系统技术选型必须遵守以下原则:

  • 轻量
  • 健壮
  • 性能优先
  • 少依赖
  • 易部署
  • 易排障

目标形态应更接近:

  • 软路由插件
  • 轻量代理
  • 边缘网关
  • 单二进制网络服务

而不是典型的重业务后台服务。

15.2 第一版推荐

核心网关建议明确采用以下技术栈:

  • 服务端语言:Go
  • 实时协议:WebSocket
  • 管理接口:HTTP
  • 消息格式:JSON
  • 运行态存储:纯内存
  • 配置方式:本地配置文件加环境变量
  • 部署形态:单二进制
  • 日志:结构化日志

推荐原因:

  • 更适合长连接、低延迟、fanout 场景
  • 内存和 CPU 占用更可控
  • 二进制部署简单
  • 依赖少,更接近基础设施服务风格
  • 后续扩到更高在线数更稳

如果目标是先稳定支撑 1000-2000 在线连接,推荐优先考虑:

  • 服务端:Go
  • 实时协议:WebSocket
  • 管理接口:HTTP
  • 配置来源:本地配置文件或环境变量
  • latest state:进程内内存

原因:

  • 长连接和 fanout 场景更贴近 Go 的强项
  • 资源占用更稳
  • 后续扩到更高并发成本更低

15.3 网关内部建议模块

为了保持“轻量但可扩展”,建议核心网关内部拆为以下模块:

  • listener
    • 管理 WebSocket 和 HTTP 入口
  • session manager
    • 管理连接、身份、心跳、会话元数据
  • router
    • 负责 topic、device、group 维度路由
  • fanout hub
    • 负责多订阅者分发
  • state cache
    • 保存设备 latest state
  • auth verifier
    • 校验 Producer、Consumer、Controller 的 token
  • rate limiter
    • 限制异常高频上报
  • plugin bus
    • 异步发布给规则、通知、归档等插件

这些模块都应保持进程内实现,避免第一版引入外部组件。

15.4 第一版不建议引入的依赖

第一版不建议引入:

  • 数据库
  • 消息队列
  • 服务注册中心
  • 复杂配置中心
  • 脚本型规则执行器
  • 重型 RPC 框架

这些能力都可以在后续容量和业务复杂度真正需要时再补。

15.5 可接受的替代方案

如果团队更熟悉 TypeScript,也可以先用:

  • Node.js + TypeScript + WebSocket

但要注意:

  • 网关层应保持非常薄
  • 不要过早塞业务逻辑
  • 一开始就准备好以后拆分插件

但如果核心目标明确是“像软路由一样稳定跑实时中转”,仍然优先推荐 Go。


16. MVP 范围

第一版只做以下内容:

16.1 核心网关

  • WebSocket 接入
  • Producer / Consumer / Controller 三类角色
  • 统一消息信封
  • 按设备订阅
  • latest state 缓存
  • 心跳和断线处理
  • 基础限流
  • 基础鉴权

16.2 模拟器接入

  • 地图拖点
  • 轨迹文件导入
  • 播放 / 暂停 / 倍速 / 循环
  • 输出位置和心率等标准 telemetry

16.3 观察端

  • 订阅设备位置和状态
  • 查看最新点
  • 接收实时事件

16.4 插件最小集

  • Recorder
    • 可选启用
  • Rule Engine
    • 先做最简单几条规则
  • Dispatcher
    • 先支持网关内消息通知

16.5 明确不做

  • 业务数据库耦合
  • 复杂多租户
  • 大报表系统
  • 历史查询中心
  • 集群调度系统

17. 第二阶段演进

第二阶段建议逐步增加:

  • 归档从客户端双写迁移到 Recorder
  • 多实例网关
  • Redis 作为共享状态和实例间消息桥
  • 更细粒度权限
  • 完整规则库
  • 回放服务独立化
  • 外部通知集成

第三阶段再考虑:

  • 数据库落盘
  • 历史检索
  • 统计分析
  • 高可用和地域分布

18. 最终结论

最终方案建议如下:

  • 以“实时设备数据网关”作为系统核心,而不是继续围绕“GPS 模拟器”扩展
  • 网关独立部署,不占主业务服务器的实时带宽和连接资源
  • 第一版采用 0 数据库设计,仅保留运行时内存态
  • 核心只做实时接入、标准化、路由、订阅和 latest state
  • 规则判定、通知分发、回放、归档全部插件化
  • 第一版先支撑 1000-2000 在线上报
  • 当前客户端可继续采用“实时发网关 + 10-30 秒批量发业务服”的过渡方案
  • 中期应演进为“客户端单写网关,归档由 Recorder 插件完成”

这套方案能同时覆盖:

  • 开发模拟
  • 家长端监控
  • 场控
  • 规则判定
  • 通知分发
  • 数据回放
  • 后续更多传感器接入

同时又能把实时性能放在系统设计的首位。