# 实时设备数据网关最终方案
> 文档版本：v1.0
> 最后更新：2026-04-02 08:28:05


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

---

## 1. 目标与定位

本系统不再定义为“GPS 模拟器”，而定义为：

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

一句话定义：

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

---

## 2. 设计原则

### 2.1 核心优先级

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

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

### 2.2 核心边界

核心服务只负责：

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

核心服务不负责：

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

---

## 3. 总体架构

### 3.1 拓扑关系

```text
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 订阅某个 `deviceId` 或 `groupId`
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 统一信封

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

```json
{
  "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

```json
{
  "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

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

### 8.3 运动 telemetry

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

### 8.4 设备状态

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

### 8.5 事件

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

### 8.6 控制命令

```json
{
  "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 插件完成”

这套方案能同时覆盖：

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

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



