From 8fd38eb87ee17a150aad71c4064554caa20845fc Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 2 Jul 2026 19:32:52 +0800 Subject: [PATCH] docs: add vehicle ingest production runbook --- docs/ops/go-service-observability.md | 2 + docs/ops/vehicle-ingest-runbook.md | 177 +++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 docs/ops/vehicle-ingest-runbook.md diff --git a/docs/ops/go-service-observability.md b/docs/ops/go-service-observability.md index 07c3ae8e..ce550738 100644 --- a/docs/ops/go-service-observability.md +++ b/docs/ops/go-service-observability.md @@ -18,6 +18,8 @@ The Go services expose local-only health and metrics endpoints. They are intende ## Quick Checks +生产事故处理顺序和阈值解释见 [车辆接入生产运行手册](vehicle-ingest-runbook.md)。 + ```bash curl -fsS http://127.0.0.1:20211/readyz curl -fsS http://127.0.0.1:20211/metrics diff --git a/docs/ops/vehicle-ingest-runbook.md b/docs/ops/vehicle-ingest-runbook.md new file mode 100644 index 00000000..9068c524 --- /dev/null +++ b/docs/ops/vehicle-ingest-runbook.md @@ -0,0 +1,177 @@ +# 车辆接入生产运行手册 + +## 范围 + +本文覆盖 ECS `115.29.187.205` 上的 Go 原生车辆接入链路: + +- GB32960 TCP 接入 +- JT/T 808 TCP 接入 +- 宇通 MQTT 接入 +- NATS 到 Kafka 桥接 +- Kafka 历史、统计、实时消费者 +- Redis 实时缓存、MySQL 实时表、TDengine 历史表 + +运行手册的目标是按自上而下的顺序定位问题:入口、队列、桥接、消费、存储、查询 API。 + +## 服务地图 + +| 层级 | 服务 | systemd 单元 | 本机端点 | +| --- | --- | --- | --- | +| 接入 | Gateway | `lingniu-go-gateway.service` | `127.0.0.1:20211` | +| 桥接 | NATS Kafka bridge | `lingniu-go-nats-kafka-bridge.service` | `127.0.0.1:20214` | +| 历史 | TDengine writer | `lingniu-go-history-writer.service` | `127.0.0.1:20212` | +| 统计 | MySQL stat writer | `lingniu-go-stat-writer.service` | `127.0.0.1:20213` | +| 实时 | Realtime API/projector | `lingniu-go-realtime-api.service` | `127.0.0.1:20200` | + +业务端口: + +- GB32960 TCP:`0.0.0.0:32960` +- JT/T 808 TCP:`0.0.0.0:808` +- 实时/API:`0.0.0.0:20200` + +## 五分钟排查顺序 + +先回答最核心的问题:数据有没有进来,有没有排队,有没有被消费,最后有没有被查询到。 + +1. 检查所有服务 `/readyz`。 +2. 检查 gateway 帧计数和 TCP 活跃连接。 +3. 检查 NATS bridge 的 pending 和 ack-pending。 +4. 检查 bridge 写 Kafka 与 NATS ack 是否同时增长。 +5. 检查 history、stat、realtime 三类 Kafka consumer lag。 +6. 检查各 writer 的写入、提交、更新计数。 +7. 最后再查存储和业务查询 API。 + +```bash +for port in 20211 20212 20213 20214 20200; do + curl -fsS "http://127.0.0.1:${port}/readyz" + echo +done + +curl -fsS http://127.0.0.1:20211/metrics \ + | grep -E 'vehicle_gateway_(active_connections|frames_total|publish_total)' + +curl -fsS http://127.0.0.1:20214/metrics \ + | grep -E 'vehicle_bridge_(nats_consumer|kafka_writes_total|nats_acks_total)' + +curl -fsS http://127.0.0.1:20212/metrics | grep vehicle_history_kafka_lag +curl -fsS http://127.0.0.1:20213/metrics | grep vehicle_stat_kafka_lag +curl -fsS http://127.0.0.1:20200/metrics | grep vehicle_realtime_kafka_lag +``` + +## 健康基线 + +当前生产环境的健康特征: + +- 所有 `/readyz` 都返回 `status=ok`。 +- `vehicle_bridge_nats_consumer_ack_pending` 为 `0`。 +- history、stat、realtime 的 Kafka lag 为 `0` 或短时间小幅波动后归零。 +- gateway 的帧计数持续增长。 +- bridge 的 Kafka write 和 NATS ack 计数同时增长。 +- writer 的成功计数增长,同时 Kafka lag 不持续扩大。 + +GB32960 和 JT/T 808 的活跃连接数受上游平台连接方式影响,不能直接等同于车辆数。突然归零或持续异常下降才是信号。 + +## 告警阈值建议 + +| 信号 | 建议阈值 | 含义 | +| --- | --- | --- | +| `/readyz` 非 ok | 立即处理 | 服务或依赖不可用。 | +| Gateway 活跃连接 | 预期有流量时某协议降为 `0` | 上游网络、监听端口或进程可能异常。 | +| `vehicle_gateway_frames_total{status!="OK"}` | 连续 2 分钟增长 | 解析器或上游报文质量异常。 | +| `vehicle_bridge_nats_consumer_ack_pending` | 连续 2 分钟 `> 0` | 消息已投递给 bridge,但 Kafka 写入后未完成 ack。 | +| `vehicle_bridge_nats_consumer_pending` | 持续增长且 `> 10000` | bridge 消费 NATS 的速度跟不上生产速度。 | +| Kafka lag | 连续 5 分钟增长或 `> 10000` | 下游 consumer 或存储存在瓶颈。 | +| Writer 成功计数 | 入口增长但 writer 不增长 | bridge、Kafka、consumer 或存储链路断开。 | + +## 事故处理路径 + +### 没有新数据 + +1. 先确认监听端口。 + +```bash +ss -lntp | grep -E ':(808|32960|20200|20211|20212|20213|20214) ' +``` + +2. 检查 gateway readiness 和日志。 + +```bash +curl -fsS http://127.0.0.1:20211/readyz +journalctl -u lingniu-go-gateway.service --since '10 minutes ago' --no-pager +``` + +3. 看 gateway 帧计数是否增长。 +4. 如果 gateway 有增长但 bridge 没增长,查 NATS publish 和 bridge 日志。 +5. 如果 bridge 写入增长但 writer 不增长,查 Kafka lag 和 consumer 日志。 + +### Kafka lag 持续增长 + +1. 先从 metrics 定位是哪个服务、哪个 topic、哪个 partition。 +2. 检查对应服务 `/readyz`。 +3. 检查存储依赖:history 看 TDengine,stat 看 MySQL,realtime 看 Redis/MySQL/TDengine。 +4. 先看日志,再决定是否重启。 + +```bash +journalctl -u lingniu-go-history-writer.service --since '10 minutes ago' --no-pager +journalctl -u lingniu-go-stat-writer.service --since '10 minutes ago' --no-pager +journalctl -u lingniu-go-realtime-api.service --since '10 minutes ago' --no-pager +``` + +Kafka 是可回放日志。只要 Kafka 还保留消息,恢复 consumer 后 lag 应该能自动追平。不要在 lag 未归零前手工补数。 + +### NATS pending 持续增长 + +1. 检查 bridge `/readyz` 和日志。 +2. 对比 `vehicle_bridge_kafka_writes_total` 和 `vehicle_bridge_nats_acks_total`。 +3. 如果 Kafka 写入失败,检查 ECS 到 Kafka broker 的网络。 +4. 如果 ack-pending 卡住且日志持续报错,只重启 bridge。 + +```bash +journalctl -u lingniu-go-nats-kafka-bridge.service --since '10 minutes ago' --no-pager +systemctl restart lingniu-go-nats-kafka-bridge.service +``` + +### Raw 有数据但实时查不到 + +1. 查 realtime Kafka lag。 +2. 查 realtime update 计数。 +3. 查 realtime API readiness。 +4. 查最新 snapshot/location API。 + +```bash +curl -fsS http://127.0.0.1:20200/readyz +curl -fsS 'http://127.0.0.1:20200/api/realtime/locations?limit=1' +curl -fsS 'http://127.0.0.1:20200/api/realtime/snapshots?limit=1' +``` + +## 重启顺序 + +优先重启最小故障层: + +1. 只有一个 consumer lag 异常时,先重启对应 writer。 +2. 实时投影或 API 异常时,重启 realtime API。 +3. bridge pending 或 ack-pending 卡住时,重启 NATS Kafka bridge。 +4. 只有入口监听、解析循环或上游连接处理异常时,才重启 gateway。 + +```bash +systemctl restart lingniu-go-history-writer.service +systemctl restart lingniu-go-stat-writer.service +systemctl restart lingniu-go-realtime-api.service +systemctl restart lingniu-go-nats-kafka-bridge.service +systemctl restart lingniu-go-gateway.service +``` + +排查前不要直接全量重启。全量重启会抹掉时间线证据,也会掩盖问题到底发生在入口、队列、存储还是查询层。 + +## 存储原则 + +运行指标保留在 `/metrics`,不要为了服务健康计数新增 MySQL 或 TDengine 业务表。 + +业务存储保持最小化: + +- raw frames 保存完整 parsed JSON,用于回放和审计。 +- realtime snapshot/location 只保存 API 需要的当前业务字段。 +- history 表保存可查询的时间序列核心字段。 +- 派生统计应能从 Kafka 或 raw history 重新计算。 + +只有当产品查询明确需要、并且指标定义稳定时,才新增持久化聚合。