本文档用于收敛当前关于 GPS 模拟、中转、监控、规则判定、回放、通知分发等讨论,给出一版可直接进入实现设计的最终方案。
本系统不再定义为“GPS 模拟器”,而定义为:
一句话定义:
一个以实时中转为核心、以插件扩展业务能力的轻量遥测网关。
核心目标按优先级排序如下:
核心服务只负责:
核心服务不负责:
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
├─ 用户与设备关系
├─ 配置管理
├─ 历史归档
└─ 业务接口
实时网关在整个系统中,不是主业务服务器的替代品,而是一层独立的实时中枢。
职责分工建议固定为:
一句话理解:
主业务服务器负责“谁是谁”,实时网关负责“现在发生了什么,发给谁看”。
Producer
Consumer
Controller
channelchannelId / producerToken / consumerTokentelemetry.location / telemetry.heart_ratedeviceId 或 groupId10-30s 批量写入业务服务器这样可以保证:
实时网关必须独立于主业务服务器部署。
这样做的原因:
主业务服务只提供控制面能力:
实时网关处理数据面:
原则上,不允许每条实时上报都回主业务服务查库。
第一版核心网关可以做到 0 数据库依赖。
这里的含义是:
即使 0 数据库,网关仍然必须保留以下运行时内存数据:
网关重启后允许丢失:
系统恢复方式:
这对实时网关是可接受的。
第一版至少满足:
1000-2000 设备同时在线上报1 Hz 实时上报能力2-5 Hz< 500 ms实时中转一定消耗网关所在服务器的带宽,但不会占用主业务服务器带宽。
不做历史存储,只能减少:
不能减少:
所有进入网关的数据统一使用一个标准信封:
{
"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": {}
}
建议只保留四类顶级 topic:
telemetry.*state.*event.*command.*示例:
telemetry.locationtelemetry.heart_ratetelemetry.motionstate.deviceevent.rule_violationevent.soscommand.replay.startcommand.simulation.stop建议显式区分数据来源模式:
realmockreplaycontrol这样业务侧可以明确识别是真实数据还是模拟/回放数据。
{
"lat": 31.2304,
"lng": 121.4737,
"altitude": 12.3,
"speed": 1.5,
"bearing": 90,
"accuracy": 8,
"coordSystem": "GCJ02"
}
最少保留字段:
timestamplatlngspeedbearingaccuracycoordSystem{
"bpm": 142,
"confidence": 0.92
}
{
"cadence": 168,
"stepCount": 3201,
"calories": 248.5,
"movementState": "run"
}
{
"online": true,
"battery": 76,
"network": "4g",
"charging": false
}
{
"eventType": "rule_violation",
"ruleId": "geo-fence-01",
"severity": "high",
"text": "leave allowed area"
}
{
"command": "simulation.start",
"args": {
"sessionId": "sim-001",
"speedRate": 2
}
}
实时网关按以下维度路由:
deviceIdgroupIdtopic示例:
device:child-001device:child-001/topic:telemetry.locationgroup:class-a/topic:event.*每个设备保留一个最新状态快照,不存历史。
用途:
如果完全不鉴权,会带来两个高风险问题:
Producer
Controller
Consumer
第一版建议:
网关本身不查数据库,只做:
复杂鉴权可以由独立鉴权服务承担。
事件处理不是核心同步主链路的一部分,而是插件。
同步主链路只做:
ingest -> normalize -> route -> fanout -> update latest state
然后异步投递到插件总线:
publish -> plugin bus -> plugin async consume
Rule Engine
Dispatcher
Recorder
Replayer
Webhook
规则判定必须在插件层执行,不进入中转核心。
原因:
阈值规则
时空规则
行为规则
规则命中后由 Dispatcher 插件执行动作:
客户端保留两条链路:
10-30s 打包一次发给业务服务器存档这个方案的优点:
客户端双写存在天然风险:
推荐第二阶段改成:
Recorder 插件按 10-30s 批量归档到业务服务器这样可以保证:
建议客户端支持策略切换:
normal
monitor
1-5s 实时上报debug
1 Hzalert
模拟器不再是一个独立特殊系统,而是一个标准 Producer。
它可以上报:
telemetry.locationtelemetry.heart_ratetelemetry.motionstate.device回放器也是 Producer。
区别只是:
replay地图拖点时不应直接瞬移发点,至少要支持:
否则业务侧会看到不真实的轨迹。
本系统技术选型必须遵守以下原则:
目标形态应更接近:
而不是典型的重业务后台服务。
核心网关建议明确采用以下技术栈:
GoWebSocketHTTPJSON推荐原因:
如果目标是先稳定支撑 1000-2000 在线连接,推荐优先考虑:
GoWebSocketHTTP原因:
为了保持“轻量但可扩展”,建议核心网关内部拆为以下模块:
listener
session manager
router
fanout hub
state cache
auth verifier
rate limiter
plugin bus
这些模块都应保持进程内实现,避免第一版引入外部组件。
第一版不建议引入:
这些能力都可以在后续容量和业务复杂度真正需要时再补。
如果团队更熟悉 TypeScript,也可以先用:
Node.js + TypeScript + WebSocket但要注意:
但如果核心目标明确是“像软路由一样稳定跑实时中转”,仍然优先推荐 Go。
第一版只做以下内容:
第二阶段建议逐步增加:
第三阶段再考虑:
最终方案建议如下:
1000-2000 在线上报这套方案能同时覆盖:
同时又能把实时性能放在系统设计的首位。