# 车辆接入生产运行手册 ## 范围 本文覆盖 ECS `115.29.187.205` 上的 Go 原生车辆接入链路: - GB32960 TCP 接入 - JT/T 808 TCP 接入 - 宇通 MQTT 接入 - NATS 到 Kafka 桥接 - Kafka 历史、统计、实时消费者 - Redis 实时缓存、MySQL 实时表、TDengine 历史表 运行手册的目标是按自上而下的顺序定位问题:入口、队列、桥接、消费、存储、查询 API。 当前生产服务、topic、表和 Redis key 的清单见 [生产数据面清单](../architecture/production-data-plane-inventory.md)。 10W 车辆容量目标、当前缺口和压测口径见 [100K 车辆接入容量基线](100k-capacity-baseline.md)。 ## 服务地图 | 层级 | 服务 | 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.v1`、`vehicle.raw.go.jt808.v1`、`vehicle.raw.go.yutong-mqtt.v1` | | `lingniu-go-stat-writer.service` | `vehicle.raw.go.gb32960.v1`、`vehicle.raw.go.jt808.v1`、`vehicle.raw.go.yutong-mqtt.v1` | | `lingniu-go-realtime-api.service` | `vehicle.raw.go.gb32960.v1`、`vehicle.raw.go.jt808.v1`、`vehicle.raw.go.yutong-mqtt.v1` | | `lingniu-go-nats-kafka-bridge.service` | NATS raw subjects 到同名 Kafka topic;`vehicle.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 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 的活跃连接数受上游平台连接方式影响,不能直接等同于车辆数。突然归零或持续异常下降才是信号。 ## 压测入口 Go 版本提供 `cmd/load-sim` 用于阶段性连接和帧写入压测。压测生产入口前必须先确认上游真实数据窗口,避免和业务流量混淆。 ```bash 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 ``` ## 告警阈值建议 | 信号 | 建议阈值 | 含义 | | --- | --- | --- | | `/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 重新计算。 只有当产品查询明确需要、并且指标定义稳定时,才新增持久化聚合。