diff --git a/DECISIONS.md b/DECISIONS.md index 337926ee..908cf277 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -51,10 +51,10 @@ - 禁用 `synchronized`,使用 `ReentrantLock` 避免虚拟线程 pinning - **Consequences**: 需开启 `jdk.VirtualThreadPinned` JFR 监控 -## ADR-008 本服务不写业务库 +## ADR-008 协议接入应用不直接写业务库 - **Status**: Accepted -- **Decision**: 所有业务表(vehicle_data_actual / history / travel / gps_data 等)不再由本服务写入 -- **Consequences**: 需要新建消费服务 `vehicle-analytics-service` 承接;迁移期采用双跑对账 +- **Decision**: GB32960、JT808、Yutong MQTT 等协议接入应用只负责收、解析、校验、规整和投递 Kafka,不直接写业务表。 +- **Consequences**: 业务落库由独立 Kafka 消费应用承接:`vehicle-history-app` 写入 TDengine 历史与 RAW JSON,`vehicle-analytics-app` 写入 MySQL `vehicle_stat_metric`。 ## ADR-009 构建工具:Maven - **Status**: Accepted diff --git a/README.md b/README.md index ff8b1606..0b85bc0f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - **协议接入统一抽象**:GB/T 32960、JT/T 808、Yutong MQTT 是默认生产接入;JT/T 1078、JSATL12 作为显式 profile 的可选能力;信达 Push 仅保留为废弃兼容模块 - **原子能力化**:每个协议一个独立 Maven 模块 + 独立 AutoConfiguration + 独立配置开关 -- **业务 / 实时彻底解耦**:本服务只负责 **收 → 解析 → 校验 → 规整 → 投递 Kafka**,不碰业务库 +- **业务 / 实时彻底解耦**:协议接入应用只负责收、解析、校验、规整和投递 Kafka;历史与统计由独立 Kafka 消费应用落库 - **高并发低延迟**:Netty + Disruptor + Java 25 虚拟线程;目标单节点 ≥ 5 万 msg/s,P99 < 50 ms - **可观测、可回放、可灰度**:统一 traceId、Envelope、幂等键、DLQ、原始报文冷存 @@ -95,7 +95,7 @@ Redis 最新状态查询不在默认 Maven reactor 中;需要时使用 `-Popti ## 核心原则 -1. **本服务不写业务库**:历史、行程、在线检测、里程统计全部由 Kafka 下游消费者实现 +1. **接入与业务落库分离**:协议接入应用只负责收、解析、校验、规整和投递 Kafka;`vehicle-history-app` 写入 TDengine 历史与 RAW JSON,`vehicle-analytics-app` 将 JT808 `daily_mileage_km` 写入 MySQL `vehicle_stat_metric` 2. **协议即插拔**:每个 `protocol-*` / `inbound-*` 都可独立开关(`lingniu.ingest..enabled`) 3. **顺序保证**:同一 VIN 严格有序(Disruptor hash + Kafka 分区 key) 4. **幂等消费**:Envelope 带 `eventId`,下游去重 diff --git a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java index 62afc740..0009ac89 100644 --- a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java +++ b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/MavenModuleProfileTest.java @@ -176,6 +176,8 @@ class MavenModuleProfileTest { .contains("command-gateway/ 可选 HTTP → 设备下行命令(optional-command-gateway profile)") .contains("vehicle-history-app/ TDengine 历史查询 + RAW JSON") .contains("vehicle-analytics-app/ JT808 每日里程指标消费") + .contains("协议接入应用只负责收、解析、校验、规整和投递 Kafka") + .contains("`vehicle-analytics-app` 将 JT808 `daily_mileage_km` 写入 MySQL `vehicle_stat_metric`") .doesNotContain("Spring Boot 3.4") .doesNotContain("Netty 4.1") .doesNotContain("HiveMQ MQTT Client") @@ -185,7 +187,9 @@ class MavenModuleProfileTest { .doesNotContain("DuckDB") .doesNotContain("Parquet") .doesNotContain("Parquet + DuckDB 文件型明细库") - .doesNotContain("车辆状态/日统计消费"); + .doesNotContain("车辆状态/日统计消费") + .doesNotContain("本服务不写业务库") + .doesNotContain("里程统计全部由 Kafka 下游消费者实现"); } @Test @@ -237,6 +241,8 @@ class MavenModuleProfileTest { .contains("JSATL12 仅保留在 `optional-attachments` profile") .contains("vehicle-state-service 仅保留在 `optional-latest-state` profile") .contains("raw-archive-store 仅保留在 `optional-raw-archive-store` profile") + .contains("协议接入应用不直接写业务库") + .contains("`vehicle-analytics-app` 写入 MySQL `vehicle_stat_metric`") .doesNotContain("event-file-store") .doesNotContain("optional-event-file-store") .doesNotContain("## ADR-004 信达 Push:保留模块但彻底重写\n- **Status**: Accepted")