feat(platform): harden telemetry pipeline and unify Semi UI workspaces
This commit is contained in:
160
docs/ops/feichi-bridge.md
Normal file
160
docs/ops/feichi-bridge.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# 飞驰 HTTP → GB/T 32960 桥接服务
|
||||
|
||||
## 目标
|
||||
|
||||
将飞驰车辆数据平台中的 20 辆 `GB_T32960` 车辆转换为 GB/T 32960.3-2016 标准 TCP 报文,并发送到岭牛内网车辆网关的 32960 端口。
|
||||
|
||||
服务必须满足:
|
||||
|
||||
- 每 10 秒发现实时增量,不重复转发平台持续返回的最后一帧;
|
||||
- 使用 `0x05` 完成平台登录,实时数据使用 `0x02`,历史补传使用 `0x03`;
|
||||
- 每帧收到内网网关成功 ACK 后才推进持久化游标;
|
||||
- 自动完成 CSRF 初始化、验证码识别、RSA 密码登录和 `R_SESS` 会话维护;
|
||||
- 源会话返回 401/403 时自动重新登录,并重放一次原 API 请求;
|
||||
- TCP 断线后重新登录,并用相同原始帧重试一次;
|
||||
- 重启后从本地游标继续,默认补齐最近 1 小时、每次查询 20 分钟;
|
||||
- 保留源接口调用、目标 ACK、错误和活跃车辆等 Prometheus 指标;
|
||||
- 不转发离线车辆反复返回的旧快照。
|
||||
- 离线车辆最后快照按源时间仅补发一次 `0x03`,不伪装成实时 `0x02`。
|
||||
|
||||
## 数据路径
|
||||
|
||||
```text
|
||||
飞驰 HTTP API
|
||||
├─ 车辆发现 vehicleRealStatuss
|
||||
├─ 实时明细 vehicleRealStatussByVId
|
||||
└─ 历史明细 hisdataQuerys
|
||||
│
|
||||
▼
|
||||
feichi-bridge
|
||||
字段映射 → 去重 → 32960 编码 → ACK 后提交游标
|
||||
│
|
||||
▼
|
||||
内网 vehicle-gateway :32960
|
||||
```
|
||||
|
||||
桥接服务当前编码 V2016 `##` 报文。源平台确认的主要字段如下:
|
||||
|
||||
| 32960 数据 | 飞驰字段 |
|
||||
|---|---|
|
||||
| 数据时间 | `2000`(兼容 `9999`) |
|
||||
| 车速、里程、挡位 | `2201`、`2202`、`2203` |
|
||||
| 加速/制动踏板 | `2208`、`2209` |
|
||||
| 运行模式、DCDC | `2213`、`2214` |
|
||||
| 充电状态、车辆状态 | `2301`、`3201` |
|
||||
| 总电压、总电流、绝缘电阻 | `2613`、`2614`、`2617` |
|
||||
| SOC | `7615` |
|
||||
| 原始经纬度 | `2502`、`2503` |
|
||||
| 单体电压、温度 | `2003`、`2103` |
|
||||
| 极值 | `2601`—`2612` |
|
||||
| 燃料电池主要数据 | `2110`—`2121` |
|
||||
| 驱动电机复合数据 | `2307`、`2308` |
|
||||
|
||||
定位必须使用 `2502/2503` 原始坐标;`.gd` 字段是页面地图转换坐标,不能写进国标数据。
|
||||
|
||||
## 自动登录与认证文件
|
||||
|
||||
服务按照网页端协议执行 `first-login → randCode → login`:读取 `x-api-csrf`/`CSRF`,验证码图片交给 ECS 本机、禁网运行的专用 OCR 容器,密码使用网页端同一 RSA 公钥做 PKCS#1 v1.5 加密,登录成功后保存响应中的 `R_SESS`。OCR 结果不会被直接信任;登录接口是最终校验,错误时会换一张验证码重试。密码、Cookie、验证码图片和响应正文均不写日志。
|
||||
|
||||
`/opt/lingniu-go-native/secrets/feichi-auth.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "replace-me",
|
||||
"password": "replace-me"
|
||||
}
|
||||
```
|
||||
|
||||
文件权限必须为 `0600`。飞驰若提供机器账号、长期 Token 或正式接口鉴权,仍应优先切换到供应商承诺的方式。
|
||||
|
||||
`/opt/lingniu-go-native/secrets/feichi-target.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"platformId": "FEICHIBRIDGE00001",
|
||||
"username": "bridge",
|
||||
"password": "replace-me"
|
||||
}
|
||||
```
|
||||
|
||||
`platformId` 必须正好 17 字节。目标网关启用 `GB32960_AUTH_MODE=enforce` 时,需在网关凭据文件中登记同一平台账号和密码。
|
||||
|
||||
## 环境配置
|
||||
|
||||
`/opt/lingniu-go-native/env/feichi-bridge.env` 最小配置:
|
||||
|
||||
```dotenv
|
||||
FEICHI_BASE_URL=http://mob.fsfeichi.com.cn:8000
|
||||
FEICHI_AUTH_SECRET_FILE=/opt/lingniu-go-native/secrets/feichi-auth.json
|
||||
FEICHI_TARGET_SECRET_FILE=/opt/lingniu-go-native/secrets/feichi-target.json
|
||||
FEICHI_TARGET_ADDR=127.0.0.1:32960
|
||||
FEICHI_STATE_FILE=/var/lib/lingniu-feichi-bridge/state.json
|
||||
HEALTH_ADDR=127.0.0.1:20219
|
||||
```
|
||||
|
||||
可调参数及默认值:
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|---|---:|---|
|
||||
| `FEICHI_POLL_INTERVAL_SECONDS` | 10 | 实时轮询间隔 |
|
||||
| `FEICHI_DISCOVERY_INTERVAL_SECONDS` | 300 | 车辆清单刷新间隔 |
|
||||
| `FEICHI_FETCH_CONCURRENCY` | 4 | 明细接口并发数 |
|
||||
| `FEICHI_SOURCE_STALE_SECONDS` | 120 | 超过该时间不作为实时帧转发 |
|
||||
| `FEICHI_STALE_REISSUE_ENABLED` | true | 将离线旧快照去重后仅补发一次为 `0x03` |
|
||||
| `FEICHI_HTTP_TIMEOUT_SECONDS` | 15 | HTTP 超时 |
|
||||
| `FEICHI_OCR_IMAGE` | `lingniu/feichi-captcha-ocr:1.0.0` | 本机验证码 OCR 镜像 |
|
||||
| `FEICHI_OCR_TIMEOUT_SECONDS` | 20 | 单次验证码识别超时 |
|
||||
| `FEICHI_LOGIN_MAX_ATTEMPTS` | 20 | 单轮自动登录最多验证码次数 |
|
||||
| `FEICHI_LOGIN_RETRY_SECONDS` | 1 | 验证码/登录失败重试间隔 |
|
||||
| `FEICHI_TARGET_TIMEOUT_SECONDS` | 10 | TCP 写入和 ACK 超时 |
|
||||
| `FEICHI_BACKFILL_ENABLED` | true | 是否启用历史补传 |
|
||||
| `FEICHI_BACKFILL_LOOKBACK_SECONDS` | 3600 | 无游标时回看范围 |
|
||||
| `FEICHI_BACKFILL_WINDOW_SECONDS` | 1200 | 单次历史查询窗口 |
|
||||
| `FEICHI_BACKFILL_SAFETY_SECONDS` | 30 | 历史与实时之间的安全延迟 |
|
||||
| `FEICHI_BACKFILL_INTERVAL_SECONDS` | 3600 | 补传巡检周期 |
|
||||
|
||||
飞驰地址当前是明文 HTTP。部署时应限制服务的出口目的地址,并避免认证文件、Cookie、响应原文进入代理访问日志。
|
||||
|
||||
## 构建与启动
|
||||
|
||||
```bash
|
||||
cd go/vehicle-gateway
|
||||
go test ./internal/feichibridge ./cmd/feichi-bridge
|
||||
CGO_ENABLED=0 go build -trimpath -o feichi-bridge ./cmd/feichi-bridge
|
||||
|
||||
docker build -t lingniu/feichi-captcha-ocr:1.0.0 ../../deploy/feichi-ocr
|
||||
install -d -m 0750 /var/lib/lingniu-feichi-bridge
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now lingniu-go-feichi-bridge
|
||||
```
|
||||
|
||||
检查:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:20219/healthz
|
||||
curl -fsS http://127.0.0.1:20219/readyz
|
||||
curl -fsS http://127.0.0.1:20219/metrics | grep vehicle_feichi_bridge
|
||||
journalctl -u lingniu-go-feichi-bridge -f
|
||||
jq '.vehicles | to_entries[] | select(.value.last_realtime_ack_at or .value.last_backfill_ack_at) |
|
||||
{vin: .key, realtime_ack: .value.last_realtime_ack_at, backfill_ack: .value.last_backfill_ack_at}' \
|
||||
/var/lib/lingniu-feichi-bridge/state.json
|
||||
```
|
||||
|
||||
## 上线验收
|
||||
|
||||
先选 1 辆在线车灰度 30 分钟,再开放全部 20 辆。验收需要同时满足:
|
||||
|
||||
1. 网关收到一次 `0x05` 登录,后续实时帧为 `0x02`;
|
||||
2. 同一源时间和同一内容只落一条,重启后无持续重复;
|
||||
3. 断开目标 TCP 后能重连、重新登录并继续收到 ACK;
|
||||
4. 使源会话过期后能自动重登;连续登录失败时 `/readyz` 变为 503,游标不前移;
|
||||
5. 两辆离线车不会把历史最后一帧当实时数据周期发送;
|
||||
6. 原始经纬度、总压、总流、SOC、单体电压和温度与平台页面抽样一致;
|
||||
7. 20 辆车连续运行 24 小时后,源/目标计数和平台记录数差异可解释。
|
||||
|
||||
## 已知边界
|
||||
|
||||
- 页面接口不是飞驰承诺的稳定开放 API,接口结构升级可能要求同步调整客户端;
|
||||
- 验证码识别依赖本机 OCR 镜像和飞驰当前验证码样式;样式变化会触发登录失败告警,不会导致未 ACK 数据推进游标;
|
||||
- 源接口只提供离散快照,无法恢复平台未保存或查询窗口之外的原始上行帧;
|
||||
- 映射结果是标准 32960 语义报文,不是车辆原始二进制报文的逐字节复制。
|
||||
@@ -49,6 +49,8 @@ Gateway 在 NATS 模式下设置 `FIELDS_DERIVE_FROM_RAW_ENABLED=true`,每帧
|
||||
|
||||
stat-writer 默认 `STATS_WORKERS=3`,每个 worker 是同一 consumer group 的独立 Kafka reader。Kafka 按车辆 key 固定分区,因此单车累计里程仍按分区顺序执行,不同分区并行。调整 worker 后同时核对 `vehicle_stat_config{setting="workers"}`、每个 `vehicle_stat_worker_active{worker}`、MySQL 连接数、write p99 和 stat lag;worker 不应超过 fields topic 的有效分区并行度。
|
||||
|
||||
stat-writer 对死锁、锁等待和网络中断等瞬时 MySQL 故障按 `STATS_RETRY_ATTEMPTS` 有限重试,重试耗尽后保持失败关闭并等待存储恢复。只有 `NULL`、数值越界、类型转换、字段过长和 CHECK 约束这类明确的单消息数据错误,才会在同样的有限重试耗尽后写入 `STATS_QUARANTINE_DIR` 并提交该 Kafka 偏移量,避免一条坏消息阻塞整个分区;表结构、权限和未知程序错误不会被隔离。隔离文件包含原 topic、partition、offset、key、value 和错误,默认权限为 `0600`,处理前不得删除。
|
||||
|
||||
history-writer 和 realtime-writer 同样默认分别使用 `HISTORY_WORKERS=3`、`REALTIME_WORKERS=3`。每个 worker 只顺序处理 Kafka 分配给自己的分区,TDengine 表缓存、MySQL 实时节流缓存和车牌缓存均支持并发访问。调整后必须同时核对对应的 `*_config{setting="workers"}`、`*_worker_active`、pending/retry、后端连接数和 Kafka lag。
|
||||
|
||||
history-writer 启动会对既有 `vehicle_locations` stable 幂等补齐 `soc_percent DOUBLE`。发布后除 `/readyz` 外,必须抽查一台支持 SOC 的车辆在 TDengine 历史位置中 `soc_percent IS NOT NULL`;如果迁移报错且不是明确的重复列错误,服务应保持未就绪,先处理 TDengine DDL/权限,不能跳过迁移后强启。
|
||||
@@ -174,6 +176,8 @@ curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_kafka_lag
|
||||
curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_samples_total
|
||||
curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_sources_total
|
||||
curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_last_
|
||||
curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_quarantine_total
|
||||
find /var/lib/lingniu-go-native/stat-writer-quarantine -maxdepth 1 -type f -printf '%f %s bytes\n'
|
||||
curl -fsS http://127.0.0.1:20216/metrics | grep vehicle_realtime_kafka_lag
|
||||
curl -fsS http://127.0.0.1:20217/metrics | grep vehicle_identity_writer_kafka_lag
|
||||
for port in 20212 20213 20216 20217 20200; do
|
||||
|
||||
Reference in New Issue
Block a user