diff --git a/docs/operations/vehicle-ingest-tdengine-verification.md b/docs/operations/vehicle-ingest-tdengine-verification.md index 603d7202..aafe8bf8 100644 --- a/docs/operations/vehicle-ingest-tdengine-verification.md +++ b/docs/operations/vehicle-ingest-tdengine-verification.md @@ -1,6 +1,6 @@ # Vehicle Ingest TDengine 链路验收手册 -这份手册用于验收当前 32960/JT808 接入重构链路: +这份手册用于验收当前 GB32960/JT808/Yutong MQTT 接入重构链路: 1. 协议 App 接收 TCP 报文。 2. 协议 App 归档原始帧元数据,并把事件信封发布到 Kafka。 @@ -34,14 +34,16 @@ export TDENGINE_TELEMETRY_FIELDS_ENABLED=false ## 构建 ```bash -mvn -pl modules/apps/jt808-ingest-app,modules/apps/gb32960-ingest-app,modules/apps/vehicle-history-app -am package -Dmaven.test.skip=true +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/jt808-ingest-app/target/jt808-ingest-app.jar` - `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` ## 启动 JT808 接入服务,监听 808 端口 @@ -81,6 +83,26 @@ curl -sS http://127.0.0.1:20100/actuator/health/readiness lsof -nP -iTCP:32960 -sTCP:LISTEN ``` +## 启动 Yutong MQTT 接入服务 + +```bash +HTTP_PORT=20500 \ +YUTONG_MQTT_ENABLED=true \ +YUTONG_MQTT_AUTO_STARTUP=true \ +KAFKA_CONSUMER_ENABLED=false \ +java -jar modules/apps/yutong-mqtt-app/target/yutong-mqtt-app.jar +``` + +验证: + +```bash +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 +``` + +预期:健康检查为 `UP`,并且 Kafka 中 `vehicle.raw.mqtt-yutong.v1`、`vehicle.event.mqtt-yutong.v1` 能随真实 MQTT 消息增长。 + ## 启动 TDengine 版历史服务 ```bash @@ -102,6 +124,26 @@ curl -sS http://127.0.0.1:20200/v3/api-docs \ 预期:健康检查为 `UP`,OpenAPI 中包含通用位置分页查询和 RAW 帧查询接口。`/api/event-history/telemetry/fields` 只有在 `TDENGINE_TELEMETRY_FIELDS_ENABLED=true` 后才会暴露;默认高吞吐模式不暴露该接口,也不会持续写入逐字段宽表。 +## 启动 JT808 指标统计服务 + +```bash +HTTP_PORT=20300 \ +KAFKA_CONSUMER_ENABLED=true \ +VEHICLE_STAT_ENABLED=true \ +VEHICLE_STAT_JT808_MILEAGE_ENABLED=true \ +java -jar modules/apps/vehicle-analytics-app/target/vehicle-analytics-app.jar +``` + +验证: + +```bash +curl -sS http://127.0.0.1:20300/actuator/health +curl -sS http://127.0.0.1:20300/actuator/health/liveness +curl -sS http://127.0.0.1:20300/actuator/health/readiness +``` + +预期:健康检查为 `UP`;JT808 位置帧中有 GPS 总里程时,服务按差值法写入 MySQL `vehicle_stat_metric` 的 `daily_mileage_km` 指标。 + ## 本机 launchctl 部署 当前本机生产验证建议使用仓库模板生成 plist: @@ -115,6 +157,7 @@ deploy/local/launchctl/ - `jt808-ingest-app` 监听 TCP `808`,HTTP `20400`。 - `gb32960-ingest-app` 监听 TCP `32960`,HTTP `20100`。 - `vehicle-history-app` 监听 HTTP `20200`。 +- Yutong MQTT 与 `vehicle-analytics-app` 可以按上面的 jar 启动命令手工验收;Portainer/ECS 生产部署已包含这两个服务。 - history 热查询只依赖 Kafka 和 TDengine `raw_frames`;本地模板不会给 history 注入 `SINK_ARCHIVE_PATH`。 - GB32960/JT808 接入服务可以继续使用 `SINK_ARCHIVE_PATH` 保存原始 `.bin` 冷备,但这不是 history API 的实时查询前置条件。 - 替换 jar 前先 `launchctl bootout`,再复制 jar 和 `launchctl bootstrap`,避免 KeepAlive 在 jar 拷贝中途重启。 @@ -138,16 +181,24 @@ 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:20300/actuator/health +curl -sS http://127.0.0.1:20300/actuator/health/liveness +curl -sS http://127.0.0.1:20300/actuator/health/readiness ``` Swagger: - JT808 ingest: `http://127.0.0.1:20400/swagger-ui/index.html` - GB32960 ingest: `http://127.0.0.1:20100/swagger-ui/index.html` +- Yutong MQTT ingest: `http://127.0.0.1:20500/swagger-ui/index.html` - History query: `http://127.0.0.1:20200/swagger-ui/index.html` +- Analytics metrics: `http://127.0.0.1:20300/swagger-ui/index.html` ## Kafka Topic 验证 @@ -157,6 +208,8 @@ Swagger: - `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 会拆成: @@ -164,6 +217,8 @@ Swagger: - `vehicle-history-gb32960-raw` - `vehicle-history-jt808-event` - `vehicle-history-jt808-raw` +- `vehicle-history-yutong-mqtt-event` +- `vehicle-history-yutong-mqtt-raw` ## JT808 实时转发验收 @@ -245,7 +300,7 @@ SELECT COUNT(*) FROM telemetry_fields; - `com.lingniu.gb32960.32960`: `gb32960-ingest-app`,TCP `32960`,HTTP `20100` - `com.lingniu.vehicle-history.jt808`: `vehicle-history-app`,HTTP `20200` -健康检查已确认三个服务均返回 `{"status":"UP"}`。 +当次健康检查均返回 `{"status":"UP"}`。 可重复运行 live 验收工具: diff --git a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java index 0f87dff7..b3bbe922 100644 --- a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java +++ b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java @@ -137,7 +137,7 @@ class PortainerComposeResourceLimitsTest { String runbook = Files.readString(repositoryRoot() .resolve("docs/operations/vehicle-ingest-tdengine-verification.md")); - for (String httpPort : List.of("20100", "20400", "20200")) { + for (String httpPort : List.of("20100", "20400", "20500", "20200", "20300")) { assertThat(runbook) .contains("curl -sS http://127.0.0.1:" + httpPort + "/actuator/health/liveness") .contains("curl -sS http://127.0.0.1:" + httpPort + "/actuator/health/readiness"); @@ -316,6 +316,29 @@ class PortainerComposeResourceLimitsTest { .doesNotContain("repackaged all three app jars"); } + @Test + void tdengineVerificationRunbookDocumentsAllActiveProductionApps() throws IOException { + String runbook = Files.readString(repositoryRoot() + .resolve("docs/operations/vehicle-ingest-tdengine-verification.md")); + String activeBuildCommand = + "mvn -pl :gb32960-ingest-app,:jt808-ingest-app,:yutong-mqtt-app,:vehicle-history-app,:vehicle-analytics-app -am package -Dmaven.test.skip=true"; + + assertThat(runbook) + .contains(activeBuildCommand) + .contains("modules/apps/gb32960-ingest-app/target/gb32960-ingest-app.jar") + .contains("modules/apps/jt808-ingest-app/target/jt808-ingest-app.jar") + .contains("modules/apps/yutong-mqtt-app/target/yutong-mqtt-app.jar") + .contains("modules/apps/vehicle-history-app/target/vehicle-history-app.jar") + .contains("modules/apps/vehicle-analytics-app/target/vehicle-analytics-app.jar") + .contains("vehicle.raw.mqtt-yutong.v1") + .contains("vehicle.event.mqtt-yutong.v1") + .contains("vehicle-history-yutong-mqtt-event") + .contains("vehicle-history-yutong-mqtt-raw") + .contains("vehicle-analytics-app") + .doesNotContain("mvn -pl modules/apps/jt808-ingest-app,modules/apps/gb32960-ingest-app,modules/apps/vehicle-history-app") + .doesNotContain("健康检查已确认三个服务"); + } + @Test void architectureDocsNameKafkaInsteadOfGenericMq() throws IOException { String decisions = Files.readString(repositoryRoot().resolve("DECISIONS.md"));