Files
lingniu-vehicle-ingest/docs/ops/vehicle-ingest-runbook.md
2026-07-03 18:28:07 +08:00

9.1 KiB
Raw Blame History

车辆接入生产运行手册

范围

本文覆盖 ECS 115.29.187.205 上的 Go 原生车辆接入链路:

  • GB32960 TCP 接入
  • JT/T 808 TCP 接入
  • 宇通 MQTT 接入
  • NATS 到 Kafka 桥接
  • Kafka 历史、统计、实时消费者
  • Redis 实时缓存、MySQL 实时表、TDengine 历史表

运行手册的目标是按自上而下的顺序定位问题:入口、队列、桥接、消费、存储、查询 API。

当前生产服务、topic、表和 Redis key 的清单见 生产数据面清单

10W 车辆容量目标、当前缺口和压测口径见 100K 车辆接入容量基线

服务地图

层级 服务 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

Topic 配置基线

生产环境的 env 文件必须和 Go topic 命名空间保持一致:

服务 必要 topic
lingniu-go-history-writer.service vehicle.raw.go.gb32960.v1vehicle.raw.go.jt808.v1vehicle.raw.go.yutong-mqtt.v1
lingniu-go-stat-writer.service vehicle.raw.go.gb32960.v1vehicle.raw.go.jt808.v1vehicle.raw.go.yutong-mqtt.v1
lingniu-go-realtime-api.service vehicle.raw.go.gb32960.v1vehicle.raw.go.jt808.v1vehicle.raw.go.yutong-mqtt.v1
lingniu-go-nats-kafka-bridge.service NATS raw subjects 到同名 Kafka topicvehicle.event.go.unified.v1 只在兼容开关打开时使用

如果 stat-writer 少消费某个 raw topic对应协议的每日里程不会进入 vehicle_daily_mileage。修正后可能出现短时间 Kafka lag这是在追补历史 backlog只要 vehicle_stat_writes_total 持续增长且 lag 下降,就是健康状态。

Realtime API 是当前态投影,默认在没有已提交 offset 时从 latest 开始消费。切换 topic 或新建 consumer group 后,不应让 realtime 追扫历史 raw backlog需要重建当前态时应使用明确的回放任务或手动 reset offset。

业务端口:

  • GB32960 TCP0.0.0.0:32960
  • JT/T 808 TCP0.0.0.0:808
  • 实时/API0.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。
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)|vehicle_async_sink'

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_pending0
  • history、stat、realtime 的 Kafka lag 为 0 或短时间小幅波动后归零。
  • gateway 的帧计数持续增长。
  • bridge 的 Kafka write 和 NATS ack 计数同时增长。
  • writer 的成功计数增长,同时 Kafka lag 不持续扩大。

GB32960 和 JT/T 808 的活跃连接数受上游平台连接方式影响,不能直接等同于车辆数。突然归零或持续异常下降才是信号。

压测入口

Go 版本提供 cmd/load-sim 用于阶段性连接和帧写入压测。压测生产入口前必须先确认上游真实数据窗口,避免和业务流量混淆。

cd /opt/lingniu-go-native/current/go/vehicle-gateway

go run ./cmd/load-sim \
  -protocol jt808 \
  -addr 127.0.0.1:808 \
  -connections 100 \
  -connect-rate 100 \
  -send-interval 10s \
  -duration 2m \
  -template 0200 \
  -send=false

告警阈值建议

信号 建议阈值 含义
/readyz 非 ok 立即处理 服务或依赖不可用。
Gateway 活跃连接 预期有流量时某协议降为 0 上游网络、监听端口或进程可能异常。
vehicle_gateway_frames_total{status!="OK"} 连续 2 分钟增长 解析器或上游报文质量异常。
vehicle_async_sink_queue_depth{sink="nats"} 持续增长且不回落 Gateway 到 NATS/Kafka 的异步 publish 队列开始积压。
vehicle_async_sink_enqueue_total{status="timeout"} 任意增长 Gateway publish 队列已满或 worker 长时间阻塞,入口可能开始丢实时性。
vehicle_async_sink_publish_total{status="error"} 连续增长 NATS/Kafka publish 失败,需要先查中间件连接和日志。
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. 先确认监听端口。
ss -lntp | grep -E ':(808|32960|20200|20211|20212|20213|20214) '
  1. 检查 gateway readiness 和日志。
curl -fsS http://127.0.0.1:20211/readyz
journalctl -u lingniu-go-gateway.service --since '10 minutes ago' --no-pager
  1. 看 gateway 帧计数是否增长。
  2. 如果 gateway 有增长但 bridge 没增长,查 NATS publish 和 bridge 日志。
  3. 如果 bridge 写入增长但 writer 不增长,查 Kafka lag 和 consumer 日志。

Kafka lag 持续增长

  1. 先从 metrics 定位是哪个服务、哪个 topic、哪个 partition。
  2. 检查对应服务 /readyz
  3. 检查存储依赖history 看 TDenginestat 看 MySQLrealtime 看 Redis/MySQL/TDengine。
  4. 先看日志,再决定是否重启。
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_totalvehicle_bridge_nats_acks_total
  3. 如果 Kafka 写入失败,检查 ECS 到 Kafka broker 的网络。
  4. 如果 ack-pending 卡住且日志持续报错,只重启 bridge。
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。
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。
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 重新计算。

只有当产品查询明确需要、并且指标定义稳定时,才新增持久化聚合。