Files
lingniu-vehicle-ingest/docs/operations/vehicle-ingest-tdengine-verification.md
2026-07-01 17:31:47 +08:00

19 KiB
Raw Blame History

Vehicle Ingest TDengine 链路验收手册

这份手册用于验收当前 GB32960/JT808/Yutong MQTT 接入重构链路:

  1. 协议 App 接收 TCP 报文。
  2. 协议 App 归档原始帧元数据,并把事件信封发布到 Kafka。
  3. vehicle-history-app 消费 Kafka。
  4. history 写入 raw、location 到 TDengine不写 telemetry_fields。
  5. Swagger/API 通过 TDengine 做历史分页查询。

每一步都必须执行命令并检查输出后,才能标记为已验证。

必需运行参数

启动服务前先设置:

export KAFKA_BROKERS=114.55.58.251:9092
export TDENGINE_HISTORY_ENABLED=true
export TDENGINE_HISTORY_DATABASE=vehicle_ts
export TDENGINE_JDBC_URL='jdbc:TAOS-WS://<tdengine-host>:6041/vehicle_ts'
export TDENGINE_DRIVER_CLASS_NAME=com.taosdata.jdbc.ws.WebSocketDriver
export TDENGINE_USERNAME=root
export TDENGINE_PASSWORD='<tdengine-password>'
export TDENGINE_MIN_IDLE=0
export TDENGINE_MAX_POOL_SIZE=32

TDENGINE_MIN_IDLE=0 用于减少冷启动时 TDengine 连接重试日志。TDengine 地址稳定后,再按生产并发调大连接池。

高吞吐生产验收只验证 TDengine raw_frames、位置表和分页查询闭环。RAW 帧的 parsedJsonmetadataJsonrawUri 直接进入 TDengineGB32960 snapshot/fields 接口基于 raw_frames 与当前解析器即时返回结构化结果;接入服务写出的原始 .bin 只作为冷备复核材料。逐字段趋势宽表由独立字段解析服务消费 Kafka RAW/事件后维护,不由 history-app 写入。

构建

mvn -pl :gb32960-ingest-app,:jt808-ingest-app,:yutong-mqtt-app,:vehicle-history-app,:vehicle-analytics-app -am package -Dmaven.test.skip=true

预期产物:

  • modules/apps/gb32960-ingest-app/target/gb32960-ingest-app.jar
  • modules/apps/jt808-ingest-app/target/jt808-ingest-app.jar
  • modules/apps/yutong-mqtt-app/target/yutong-mqtt-app.jar
  • modules/apps/vehicle-history-app/target/vehicle-history-app.jar
  • modules/apps/vehicle-analytics-app/target/vehicle-analytics-app.jar

ECS 服务状态验证

生产服务只在 ECS 上运行,通过 Portainer/Docker 管理。不要在本机用 java -jar、plist 或其它守护进程方式启动 GB32960、JT808、Yutong MQTT、history、analytics。

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' \
  | egrep 'gb32960|jt808|yutong|vehicle-history|vehicle-analytics|NAMES'

ss -lntp | egrep ':(808|32960|20100|20200|20310|20400|20500)\b'

curl -sS http://127.0.0.1:20400/actuator/health
curl -sS http://127.0.0.1:20400/actuator/health/liveness
curl -sS http://127.0.0.1:20400/actuator/health/readiness
curl -sS http://127.0.0.1:20100/actuator/health
curl -sS http://127.0.0.1:20100/actuator/health/liveness
curl -sS http://127.0.0.1:20100/actuator/health/readiness
curl -sS http://127.0.0.1:20500/actuator/health
curl -sS http://127.0.0.1:20500/actuator/health/liveness
curl -sS http://127.0.0.1:20500/actuator/health/readiness
curl -sS http://127.0.0.1:20200/actuator/health
curl -sS http://127.0.0.1:20200/actuator/health/liveness
curl -sS http://127.0.0.1:20200/actuator/health/readiness
curl -sS http://127.0.0.1:20200/v3/api-docs \
  | grep -E '/api/event-history/locations|/api/event-history/raw-frames'
curl -sS http://127.0.0.1:20310/actuator/health
curl -sS http://127.0.0.1:20310/actuator/health/liveness
curl -sS http://127.0.0.1:20310/actuator/health/readiness

预期:五个容器均为运行状态;健康检查为 UPJT808/GB32960 TCP 端口在 ECS 上监听OpenAPI 中包含通用位置分页查询和 RAW 帧查询接口。history-app 不暴露 /api/event-history/telemetry/fields也不持续写入逐字段宽表。JT808 位置帧中有 GPS 总里程时,vehicle-analytics-app 按差值法写入 MySQL vehicle_stat_metricdaily_mileage_km 指标。

运行位置约束

生产身份绑定固定使用 MySQL不再提供 file/memory/sqlite 运行时切换入口。生产服务固定运行在 ECS/Portainer不提供本机守护进程模板。

  • jt808-ingest-app 监听 TCP 808HTTP 20400
  • gb32960-ingest-app 监听 TCP 32960HTTP 20100
  • yutong-mqtt-app 监听 HTTP 20500
  • vehicle-history-app 监听 HTTP 20200
  • vehicle-analytics-app 监听 HTTP 20310
  • history 热查询只依赖 Kafka 和 TDengine raw_frameshistory 不需要共享 archive volume。
  • GB32960/JT808/Yutong MQTT 接入服务可以继续使用 SINK_ARCHIVE_PATH 保存原始冷备,但这不是 history API 的实时查询前置条件。

JT808 注册身份绑定:

  • 生产需要把 0x0100 注册信息维护到 MySQL 时,只需要配置 VEHICLE_IDENTITY_MYSQL_* 连接参数。
  • 需要提供 VEHICLE_IDENTITY_MYSQL_JDBC_URLVEHICLE_IDENTITY_MYSQL_USERNAMEVEHICLE_IDENTITY_MYSQL_PASSWORD
  • 服务启动时自动创建 vehicle_identity_bindingvehicle_identity_binding_registration 两张表。vehicle_identity_binding 只维护 platevin 两列;vehicle_identity_binding_registrationprotocol + phone 保存 JT808 0x0100 注册字段。
  • 注册帧无真实 VIN 时也会写入 registration 表,vin 默认为 unknown。外部系统识别车辆后只需要维护车牌和 VIN例如INSERT INTO vehicle_identity_binding (plate, vin) VALUES ('沪A61559F', 'LNVIN000000000001') ON DUPLICATE KEY UPDATE vin = VALUES(vin);
  • MySQL 绑定在启动时加载到内存索引,plate/vin 反写后会按 VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL 周期刷新,默认 60s;帧解析热路径只查内存,不按帧访问 MySQL。
  • 后续同一终端上报 raw/event 时,服务会用 registration 表里的 phone/device_id/plate 加绑定表里的 plate/vin 解析成真实 VIN。

健康检查:

curl -sS http://127.0.0.1:20400/actuator/health
curl -sS http://127.0.0.1:20400/actuator/health/liveness
curl -sS http://127.0.0.1:20400/actuator/health/readiness
curl -sS http://127.0.0.1:20100/actuator/health
curl -sS http://127.0.0.1:20100/actuator/health/liveness
curl -sS http://127.0.0.1:20100/actuator/health/readiness
curl -sS http://127.0.0.1:20500/actuator/health
curl -sS http://127.0.0.1:20500/actuator/health/liveness
curl -sS http://127.0.0.1:20500/actuator/health/readiness
curl -sS http://127.0.0.1:20200/actuator/health
curl -sS http://127.0.0.1:20200/actuator/health/liveness
curl -sS http://127.0.0.1:20200/actuator/health/readiness
curl -sS http://127.0.0.1:20310/actuator/health
curl -sS http://127.0.0.1:20310/actuator/health/liveness
curl -sS http://127.0.0.1:20310/actuator/health/readiness

API / Swagger

  • JT808 ingest health: http://127.0.0.1:20400/actuator/health
  • GB32960 ingest health: http://127.0.0.1:20100/actuator/health
  • Yutong MQTT ingest health: http://127.0.0.1:20500/actuator/health
  • History query: http://127.0.0.1:20200/swagger-ui/index.html
  • Analytics metrics: http://127.0.0.1:20310/swagger-ui/index.html

Kafka Topic 验证

历史服务必须消费事件 topic 和 raw topic

  • vehicle.event.gb32960.v1
  • vehicle.raw.gb32960.v1
  • vehicle.event.jt808.v1
  • vehicle.raw.jt808.v1
  • vehicle.event.mqtt-yutong.v1
  • vehicle.raw.mqtt-yutong.v1

使用 Kafka CLI 或管理工具检查 topic 是否存在,并确认 history consumer group 在测试流量后没有持续堆积。默认 KAFKA_GROUP_HISTORY=vehicle-history 时,实际 group 会拆成:

  • vehicle-history-gb32960-event
  • vehicle-history-gb32960-raw
  • vehicle-history-jt808-event
  • vehicle-history-jt808-raw
  • vehicle-history-yutong-mqtt-event
  • vehicle-history-yutong-mqtt-raw

JT808 实时转发验收

如果外部平台已经把 JT808 报文转发到 ECS TCP 808,至少观察 60 秒日志和 Kafka 数据。

必须拿到以下证据:

  • jt808-ingest-app 日志显示接入连接或解析到上游消息 ID。
  • Kafka 的 vehicle.event.jt808.v1vehicle.raw.jt808.v1 有新增记录。
  • vehicle-history-app 日志没有持续出现 consumer 或 TDengine 写入失败。

消费完成后,使用已知终端手机号查询:

curl -sS 'http://127.0.0.1:20200/api/event-history/jt808/locations?phone=<phone>&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&limit=10'

预期:响应包含 items,并且每条记录包含 eventTimephonelongitudelatitudespeedKmhrawUrimetadataJson

GB32960 Snapshot / 字段投影验收

默认高吞吐模式下GB32960 全字段查询不依赖 telemetry_fields 宽表。history 先查 TDengine raw_frames,直接使用 RAW 行中的 parsedJson/parsedFields 生成 snapshot 和字段投影:

curl -sS 'http://127.0.0.1:20200/api/event-history/gb32960/snapshots?vin=<vin>&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&limit=10'

curl -sS 'http://127.0.0.1:20200/api/event-history/gb32960/snapshots/fields?vin=<vin>&fields=VEHICLE.speedKmh,VEHICLE.totalMileageKm,POSITION_V2016.longitude,POSITION_V2016.latitude&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&limit=10'

预期snapshot 返回 sourceFrames.rawArchiveUri 和解析后的 blocks;字段投影返回所选字段。新增协议字段后,先通过 replay 或专用解析任务补写 raw_frames.parsedJson,再基于历史 RAW 结构化结果查询。

TDengine 直接检查

使用 TDengine CLI 或 JDBC 客户端检查超级表和子表是否创建:

USE vehicle_ts;
SHOW STABLES;
SELECT COUNT(*) FROM raw_frames;
SELECT COUNT(*) FROM vehicle_locations;
SELECT COUNT(*) FROM jt808_locations;

预期:

  • raw_framesvehicle_locationsjt808_locations 存在;有对应协议实时流量后,计数持续增长。
  • 字段趋势宽表不属于 history-app 验收范围,由独立字段解析服务负责。
  • 有实时流量后,计数持续增加。

失败语义

  • TDengine 不可达时TDengine 查询接口应该返回 HTTP 503,消息类似 tdengine history query failed
  • TDengine 未启用时TDengine 专属接口不应该出现在 OpenAPI 中。
  • 路径不存在时API 层应该返回 HTTP 404,而不是泛化的存储失败。

2026-06-29 Superseded Local Live Verification Record

该记录仅保留为解析和存储行为的历史证据;生产运行位置已经收敛到 ECS/Portainer。

当时参与验证的服务:

  • jt808-ingest-appTCP 808HTTP 20400
  • gb32960-ingest-appTCP 32960HTTP 20100
  • vehicle-history-appHTTP 20200

当次健康检查均返回 {"status":"UP"}

可重复运行 live 验收工具:

python3 tools/vehicle_ingest_live_verify.py \
  --tdengine-rest-url 'http://115.29.185.82:6041/rest/sql/vehicle_ts' \
  --tdengine-username root \
  --tdengine-password '<tdengine-password>' \
  --history-base-url 'http://127.0.0.1:20200' \
  --date-from '2026-06-29 00:00:00' \
  --date-to '2026-06-29 23:59:59' \
  --jt808-peer-like '222.66.200.68:%' \
  --gb32960-peer-like '115.29.187.205:%'

该工具输出 passwarnfail:当前正式 GB32960 只有平台登录、没有车辆 0x02 时会输出 warn,避免把平台在线误判为车辆数据在线。正式车辆数据验收时增加 --require-gb32960-vehicle-realtime,缺少非测试 VIN 的 0x02 会直接返回非 0 退出码。

JT808 真实转发链路已验证:

  • ECS TCP 808 有外部平台 222.66.200.68 持续发送 JT808 报文。
  • vehicle.raw.jt808.v1vehicle.event.jt808.v1 被 history 消费。
  • raw_framesprotocol='JT808' 的真实行数已超过 36000
  • 外部平台真实 0x0100 注册帧已超过 347 条,真实 0x0200 位置帧已超过 12400 条。
  • jt808_locations 中位置行数已超过 8783
  • 当前高吞吐默认链路不维护 telemetry_fieldsJT808 明细验证以 raw_framesvehicle_locations 和 API 分页结果为准。
  • 分页 API 已用终端号 13079963301 验证第一页和 nextCursor 第二页,样例位置包含 longitude=118.913846latitude=31.927309statusFlag=3archive://2026/06/29/JT808/... 原始帧引用。
  • 注册帧样例:终端号 13079963320、deviceId 9963320、deviceType SEG-9888G、plate 沪A61559F、maker 70112、province 31、city 113、plateColor 2。生产 VIN 反写依赖 MySQL identity store后续 raw/event 的 VIN 解析也从 MySQL 绑定读取。

JT808 ECS smoke / 轻压测工具

仓库提供 tools/jt808_e2e_smoke.py,用于重复验证 TCP 808 到 TDengine history 的闭环:

export TDENGINE_REST_URL='http://<tdengine-host>:6041/rest/sql/vehicle_ts'
export TDENGINE_USERNAME='root'
export TDENGINE_PASSWORD='<tdengine-password>'

python3 tools/jt808_e2e_smoke.py \
  --connection-mode session \
  --start-phone 13079962000 \
  --frames 3 \
  --expect-history-count 3 \
  --verify-pagination \
  --archive-root "$PROJECT_ROOT/data/archive-jt808" \
  --tdengine-rest-url "$TDENGINE_REST_URL" \
  --tdengine-username "$TDENGINE_USERNAME" \
  --tdengine-password "$TDENGINE_PASSWORD"

默认 --connection-mode per-frame,适合单帧连接 smoke。生产验收建议使用 --connection-mode session 覆盖同一连接内连续位置帧。脚本会输出发送数量、history 可见数量、分页验证结果、raw archive 检查结果和 tdengineRawFramestdengineRawFrames 表示本次可见位置记录中的唯一 rawUri 有多少个已确认写入 TDengine raw_frames

  • 合成 0x0100 注册帧终端号 13079969999 已验证TCP 808 返回 0x8100 注册 ACKraw_frames.metadata_json 包含 jt808.register.provincejt808.register.cityjt808.register.makerjt808.register.deviceTypejt808.register.deviceIdjt808.register.plateColorjt808.register.plate,对应 raw archive 文件存在。

复查 SQL

USE vehicle_ts;
SELECT COUNT(*) FROM raw_frames WHERE protocol = 'JT808';
SELECT COUNT(*) FROM jt808_locations WHERE protocol = 'JT808';
SELECT event_time, vehicle_key, phone, message_id, raw_uri
  FROM raw_frames
  WHERE protocol = 'JT808'
  ORDER BY event_time DESC
  LIMIT 5;
SELECT ts, frame_id, phone, message_id, metadata_json, raw_uri
  FROM raw_frames
  WHERE protocol = 'JT808' AND phone = '13079969999' AND message_id = 256
  ORDER BY ts DESC
  LIMIT 3;

复查 API

curl -sS 'http://127.0.0.1:20200/api/event-history/jt808/locations?phone=13079963301&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&order=DESC&limit=3'
curl -sS 'http://127.0.0.1:20200/api/event-history/jt808/raw-frames?phone=13079963320&messageId=256&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&order=DESC&limit=3'

/api/event-history/jt808/raw-frames 用于排查注册、鉴权、心跳、位置等原始帧索引。返回项会保留 messageIdmessageIdHexrawUriparseStatuspeervinphone 和完整 metadataJson;注册帧的 metadataJson 包含 jt808.register.provincejt808.register.cityjt808.register.makerjt808.register.deviceTypejt808.register.deviceIdjt808.register.plateColorjt808.register.plateidentitySourceidentityResolved 等字段。

GB32960 链路已用正式平台登录和合成实时帧验证:

  • 正式平台 115.29.187.205 已连接 TCP 32960,服务收到 0x05 PLATFORM_LOGIN 并返回 ACK日志显示账号 Hyundai、策略 ALLOW
  • 2026-06-29 正式平台登录 raw 已有 3 条,均落入 TDengine raw_frames,可通过 RAW 查询看到 parsedJson 中的 PLATFORM_LOGIN 解析结果。
  • 当前正式平台尚未发送车辆实时上报 0x02;非测试 VIN 的 message_id=2 计数为 0,因此不能把平台登录误判为车辆数据在线。
  • TCP 32960 返回 GB32960 2323 ACK。
  • raw_frames 中测试 VIN LTEST202606290001 已有 2 条 raw 记录。
  • 当前默认通过 GB32960 snapshot/fields 即时解析 raw_frames 中的 RAW JSON不维护字段宽表增长。
  • latest raw 包含 archive://2026/06/29/GB32960/LTEST202606290001/...
  • snapshots API 可查到 speedKmh=62.4totalMileageKm=223456.7longitude=116.397128latitude=39.916527

复查 SQL

USE vehicle_ts;
SELECT COUNT(*) FROM raw_frames
  WHERE protocol = 'GB32960' AND vin = 'LTEST202606290001';
SELECT event_time, vin, raw_uri, frame_id
  FROM raw_frames
  WHERE protocol = 'GB32960' AND vin = 'LTEST202606290001'
  ORDER BY event_time DESC
  LIMIT 5;
SELECT ts, vin, message_id, event_time, raw_uri, peer, metadata_json
  FROM raw_frames
  WHERE protocol = 'GB32960' AND peer LIKE '115.29.187.205:%'
  ORDER BY ts DESC
  LIMIT 5;
SELECT COUNT(*) FROM raw_frames
  WHERE protocol = 'GB32960' AND message_id = 2 AND vin <> '' AND vin NOT LIKE 'LTEST%';

也可以直接运行仓库 smoke 工具验证 TCP 32960、GB32960 snapshot/fields、TDengine raw_frames。如果需要复核冷备 .bin,再传入接入服务 archive 根目录:

export TDENGINE_REST_URL='http://<tdengine-host>:6041/rest/sql/vehicle_ts'
export TDENGINE_USERNAME='root'
export TDENGINE_PASSWORD='<tdengine-password>'

python3 tools/gb32960_e2e_smoke.py \
  --archive-root "$PROJECT_ROOT/data/archive-jt808" \
  --tdengine-rest-url "$TDENGINE_REST_URL" \
  --tdengine-username "$TDENGINE_USERNAME" \
  --tdengine-password "$TDENGINE_PASSWORD"

预期输出包含 records >= 1fieldCounts 中关键字段均大于 0、tdengineRawFrames 等于本次可见唯一 rawUri 数量。传入 --archive-root 时,archiveChecked >= 1 说明冷备文件也能复核;这里的 records 字段表示 snapshot 数量,用于兼容早期脚本输出名。

复查 API

curl -sS 'http://127.0.0.1:20200/api/event-history/gb32960/snapshots?vin=LTEST202606290001&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&limit=10'
curl -sS 'http://127.0.0.1:20200/api/event-history/gb32960/snapshots/fields?vin=LTEST202606290001&fields=VEHICLE.speedKmh,VEHICLE.totalMileageKm,POSITION_V2016.longitude,POSITION_V2016.latitude&dateFrom=2026-06-29T00:00:00%2B08:00&dateTo=2026-06-29T23:59:59%2B08:00&limit=10'

当前注意事项

  • 当前生产链路使用 TDengine WebSocket JDBC例如 jdbc:TAOS-WS://<tdengine-host>:6041/vehicle_ts
  • KAFKA_CONSUMER_AUTO_OFFSET_RESET=latest 适合生产接入新流量;如果要回放历史 Kafka 数据,需要切换 consumer group 或重置 offset。
  • vehicle-history-app raw 和 event 使用不同 consumer bindingraw consumer 必须开启,否则 raw_frames 不会持续增长。
  • JT808 设备没有 VIN 映射时,vehicle_key 使用 jt808:<phone>;启用 MySQL identity store 后registration 表中已反写 VIN 的 phone/deviceId/plate 会用于后续 raw/event 的 VIN 解析。
  • GB32960 正式对端目前只验证到平台登录;等待真实车辆 0x02 到达后,再用非测试 VIN 复查 vehicle_locations、snapshot/fields API 和导出能力。