docs: clarify kafka-only production channel

This commit is contained in:
lingniu
2026-07-01 12:24:25 +08:00
parent e81d8dcab5
commit 3d9a520c3c
4 changed files with 34 additions and 6 deletions

View File

@@ -18,7 +18,7 @@
| 框架 | Spring Boot 3.5.3 |
| 网络 | Netty 4.2.9.Final |
| 并发 | Disruptor 4 + 虚拟线程 + `StructuredTaskScope` |
| 消息队列 | **Kafka**vin 分区,保证单车有序) |
| 事件流 | **Kafka**唯一生产消息通道,vin 分区,保证单车有序) |
| 线上消息格式 | **Protobuf**,调试走 JSON |
| MQTT 客户端 | Eclipse Paho 1.2.5 |
| 会话索引 | Redis |
@@ -96,7 +96,8 @@ Redis 最新状态查询不在默认 Maven reactor 中;需要时使用 `-Popti
## 核心原则
1. **接入与业务落库分离**:协议接入应用只负责收、解析、校验、规整和投递 Kafka`vehicle-history-app` 写入 TDengine 历史与 RAW JSON`vehicle-analytics-app` 将 JT808 `daily_mileage_km` 写入 MySQL `vehicle_stat_metric`
2. **协议即插拔**:每个 `protocol-*` / `inbound-*` 都可独立开关(`lingniu.ingest.<name>.enabled`
3. **顺序保证**同一 VIN 严格有序Disruptor hash + Kafka 分区 key
4. **幂等消费**Envelope 带 `eventId`,下游去重
5. **原始可回放**协议接入会把 `rawArchiveKey/rawArchiveUri` 追加到事件 metadataKafka Envelope、TDengine `raw_frames` 和导出都使用 `archive://...` 逻辑 URI 追溯原始 bytes实际文件由 `sink-archive` 管理
2. **Kafka 唯一消息通道**:生产链路不提供 RabbitMQ、RocketMQ 或其他 MQ 后端;接入、历史、统计统一通过 Kafka Envelope 解耦
3. **协议即插拔**每个 `protocol-*` / `inbound-*` 都可独立开关(`lingniu.ingest.<name>.enabled`
4. **顺序保证**同一 VIN 严格有序Disruptor hash + Kafka 分区 key
5. **幂等消费**Envelope 带 `eventId`,下游去重
6. **原始可回放**:协议接入会把 `rawArchiveKey/rawArchiveUri` 追加到事件 metadataKafka Envelope、TDengine `raw_frames` 和导出都使用 `archive://...` 逻辑 URI 追溯原始 bytes实际文件由 `sink-archive` 管理

View File

@@ -56,6 +56,8 @@ java -jar modules/apps/jt808-ingest-app/target/jt808-ingest-app.jar
```bash
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
lsof -nP -iTCP:808 -sTCP:LISTEN
```
@@ -74,6 +76,8 @@ java -jar modules/apps/gb32960-ingest-app/target/gb32960-ingest-app.jar
```bash
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
lsof -nP -iTCP:32960 -sTCP:LISTEN
```
@@ -90,6 +94,8 @@ java -jar modules/apps/vehicle-history-app/target/vehicle-history-app.jar
```bash
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'
```
@@ -127,8 +133,14 @@ JT808 注册身份绑定:
```bash
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: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
```
Swagger

View File

@@ -170,6 +170,7 @@ class MavenModuleProfileTest {
.contains("Netty " + projectProperty(pom, "netty.version"))
.contains("Eclipse Paho " + projectProperty(pom, "paho-mqtt.version"))
.contains("session-core/ 设备会话 + 鉴权 + Token + Redis SessionStore")
.contains("| 事件流 | **Kafka**唯一生产消息通道vin 分区,保证单车有序) |")
.contains("protocol-jt1078/ JT/T 1078optional-command-gateway profile")
.contains("protocol-jsatl12/ 苏标主动安全报警附件optional-attachments profile")
.contains("vehicle-state-service/ Kafka 全字段事件消费 + Redis 热状态查询optional-latest-state profile")
@@ -177,6 +178,7 @@ class MavenModuleProfileTest {
.contains("vehicle-history-app/ TDengine 历史查询 + RAW JSON")
.contains("vehicle-analytics-app/ JT808 每日里程指标消费")
.contains("协议接入应用只负责收、解析、校验、规整和投递 Kafka")
.contains("生产链路不提供 RabbitMQ、RocketMQ 或其他 MQ 后端")
.contains("`vehicle-analytics-app` 将 JT808 `daily_mileage_km` 写入 MySQL `vehicle_stat_metric`")
.doesNotContain("Spring Boot 3.4")
.doesNotContain("Netty 4.1")
@@ -189,7 +191,8 @@ class MavenModuleProfileTest {
.doesNotContain("Parquet + DuckDB 文件型明细库")
.doesNotContain("车辆状态/日统计消费")
.doesNotContain("本服务不写业务库")
.doesNotContain("里程统计全部由 Kafka 下游消费者实现");
.doesNotContain("里程统计全部由 Kafka 下游消费者实现")
.doesNotContain("| 消息队列 |");
}
@Test

View File

@@ -111,6 +111,18 @@ class PortainerComposeResourceLimitsTest {
}
}
@Test
void tdengineVerificationRunbookChecksLivenessAndReadinessForManagedApps() throws IOException {
String runbook = Files.readString(repositoryRoot()
.resolve("docs/operations/vehicle-ingest-tdengine-verification.md"));
for (String httpPort : List.of("20100", "20400", "20200")) {
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");
}
}
@Test
void dockerEntrypointExecsJavaForSignalHandling() throws IOException {
String dockerfile = Files.readString(repositoryRoot().resolve("Dockerfile"));