Seongwonp/vehicle-telemetry-platform
GitHub: Seongwonp/vehicle-telemetry-platform
基于 MQTT/Kafka 的网联车辆遥测数据实时采集与监控平台,集成规则与机器学习异常检测及可视化仪表板。
Stars: 1 | Forks: 2
# 车辆遥测数据采集与监控平台
## 项目概述
这是一个 IoT 后端平台,通过 MQTT 接收来自 OBD-II dongle 或模拟器的车辆传感器数据,利用 Kafka 进行分布式处理后,执行异常检测和监控。
- **核心关键词**:实时流处理、海量数据处理、IoT 后端、网联车
- **开发周期**:2026.01 ~ 进行中
- **开发者**:박성원 (Park Sungwon)
- **移动端 App 仓库**:https://github.com/Seongwonp/vehicle-telemetry-app (Flutter,使用此后端的 API)
## 系统架构
```
flowchart TD
subgraph Vehicle["차량 / 시뮬레이터"]
OBD["OBD-II 동글\n(ELM327)"]
SIM["Python 시뮬레이터\n(vehicle_simulator.py)"]
end
subgraph Broker["MQTT 브로커"]
MQ["Eclipse Mosquitto\n포트 1883 / 8883(TLS)"]
end
subgraph SpringBoot["Spring Boot (Java 17)"]
MQTT_H["MqttMessageHandler\n@ServiceActivator"]
PROD["TelemetryProducer\n파티션 키: vehicle_id"]
CONS_S["TelemetryConsumer\ntelemetry-storage-group"]
CONS_A["TelemetryConsumer\nanomalydetector-group"]
API["REST API\nJWT + Rate Limiting"]
end
subgraph Kafka["Apache Kafka"]
T1["vehicle-telemetry\n파티션 3개"]
T2["vehicle-anomaly-alerts\n파티션 3개"]
end
subgraph Storage["데이터 저장"]
INFLUX["InfluxDB\n시계열 센서 데이터"]
PG["PostgreSQL\n차량 메타 + 이상 이력"]
REDIS["Redis\nRate Limit / BruteForce"]
end
subgraph AnomalyDetector["Python 이상 감지"]
RULES["rules.py\n룰 기반 즉시 판단"]
ML["ml_detector.py\nIsolation Forest"]
NOTIFY["notifier.py\nWebhook 알림"]
end
subgraph Monitoring["모니터링"]
PROM["Prometheus\nActuator 메트릭 수집"]
GRAFANA["Grafana\n대시보드 시각화"]
end
OBD -->|"MQTT publish"| MQ
SIM -->|"MQTT publish"| MQ
MQ -->|"Spring Integration"| MQTT_H
MQTT_H --> PROD
PROD -->|"vehicle_id 키"| T1
T1 -->|"storage-group"| CONS_S
T1 -->|"anomaly-group"| CONS_A
CONS_S --> INFLUX
CONS_S --> PG
CONS_A -->|"Kafka Consumer"| RULES
CONS_A -->|"Kafka Consumer"| ML
RULES -->|"이상 감지 시"| T2
ML -->|"이상 감지 시"| T2
T2 --> NOTIFY
T2 -->|"anomaly-storage-group"| PG
API --- INFLUX
API --- PG
API --- REDIS
PROM -->|"scrape /actuator/prometheus"| SpringBoot
PROM --> GRAFANA
INFLUX --> GRAFANA
```
## 技术栈
| 领域 | 技术 |
|------|------|
| 数据接收 | MQTT (Eclipse Mosquitto) |
| 消息队列 | Apache Kafka |
| 后端 API | Java 17 + Spring Boot 3 |
| 异常检测 | Python 3.11 (基于规则 + scikit-learn) |
| 时序数据库 | InfluxDB |
| 关系型数据库 | PostgreSQL |
| 缓存 | Redis |
| 监控 | Grafana + Prometheus |
| 车辆模拟器 | Python / C |
| 安全 | JWT, TLS/SSL, CORS, Rate Limiting, 异常访问检测 |
| 基础设施 | Docker Compose, AWS EC2 (可选) |
## 采集数据规格 (基于 OBD-II)
```
{
"vehicle_id": "KR-GA-1234",
"timestamp": "2026-05-09T10:00:00Z",
"speed": 87.3,
"rpm": 2400,
"engine_temp": 92.1,
"throttle_position": 34.5,
"fuel_level": 67.0,
"battery_voltage": 13.8,
"gps": {
"lat": 37.123456,
"lng": 127.654321
},
"dtc_codes": []
}
```
## 目录结构
```
vehicle-telemetry-platform/
├── simulator/ # 차량 데이터 시뮬레이터 (Python/C)
├── broker/ # Mosquitto MQTT 브로커 설정
├── kafka/ # Kafka 설정 및 토픽 초기화 스크립트
├── backend/ # Spring Boot API 서버
│ ├── src/
│ └── build.gradle
├── anomaly-detector/ # Python 이상 감지 모듈
├── monitoring/ # Grafana + Prometheus 설정
├── docs/ # 개발 일지 및 설계 문서
├── docker-compose.yml
├── .env.example
└── README.md
```
## 实施阶段 (Phase)
| Phase | 内容 | 状态 |
|-------|------|------|
| 1 | 数据采集 pipeline (模拟器 → MQTT → Kafka → InfluxDB) | 完成 |
| 2 | REST API 服务器 (Spring Boot + JWT + Rate Limiting) | 完成 |
| 3 | 异常检测 (基于规则 + Isolation Forest ML) | 完成 |
| 4 | 安全强化 (准备 X.509, 阻断 BruteForce, 审计日志) | 完成 |
| 5 | 监控与部署 (Grafana + Prometheus + Docker Compose) | 完成 |
| 6 | Bug 修复 (Actuator 身份验证绕过/信息泄露,强化异常处理) | 完成 |
| 7 | Refresh Token + 注销失效 (基于 Redis) | 完成 |
| 8 | 数据 pipeline 稳定性 (InfluxDB 批量写入, Kafka DLQ) | 完成 |
| 9 | AI 诊断 (Gemini API) | 完成 |
| 10 | 实际启用 MQTT mTLS (后端/模拟器客户端代码,默认仍为明文) | 完成 |
| 11 | 集成 Flutter 移动端 App — 添加 CORS 支持以允许在浏览器 (Flutter Web) 中调用 API | 完成 |
## 未来计划
| 项目 | 内容 |
|------|------|
| AWS EC2 部署 | 基于 Docker Compose 部署到实际服务器 (或 Render 免费套餐) |
| 多用户支持 | 当前 admin 单一账号 → 替换为基于数据库的用户管理。实施时还需同时进行车主验证 (阻断 IDOR) |
| WebSocket 实时仪表板 | 将 REST 轮询 → 转换为 WebSocket 推送以最小化延迟 |
| DLQ 重处理 consumer | 当前 DLQ 仅用于防丢失/隔离 — 尚未实现重处理自动化 |
| 阻止 MQTT 1883 端口运营 | mTLS 代码已在 Phase 10 完成。在实际部署运营时,颁发证书 + 启用标志 + 阻断 1883 是部署阶段的任务 |
| 完善 docker-compose 环境变量传递 | `ADMIN_USERNAME`/`ADMIN_PASSWORD`/`RATE_LIMIT_RPM`/`CORS_ALLOWED_ORIGINS` 在 `docker-compose.yml` 的 backend 服务 `environment` 中缺失,因此即使更改 `.env` 值,容器中仍会应用 Spring 的默认值 |
## 故障场景与行为
总结了在实际运营中可能发生的各种故障场景下系统的具体行为。
### 场景 1 — Kafka broker 宕机
| 步骤 | 行为 |
|------|------|
| 发生故障 | Spring Boot `TelemetryProducer` 的 `kafkaTemplate.send()` 失败 |
| 直接影响 | 车辆数据无法传送到 InfluxDB/异常检测 |
| MQTT 接收 | Mosquitto 继续独立运行。数据可到达 Spring Boot |
| 恢复时 | 重启 Kafka 后 Spring Boot 会自动重连,之后从接收到的数据开始正常处理 |
| 未实现限制 | Kafka 宕机期间接收的消息将会丢失。可以通过引入 DLQ (Dead Letter Queue) 来解决 |
### 场景 2 — Python 异常检测服务宕机
| 步骤 | 行为 |
|------|------|
| 发生故障 | `anomaly-detector` 容器关闭 |
| 直接影响 | 异常检测中断,Webhook 通知中断 |
| 数据 pipeline | `telemetry-storage-group` 是独立的 Consumer Group,因此 InfluxDB 存储不受影响继续进行 |
| Kafka 消息 | `anomaly-detector-group` 的 offset 保持暂停状态 — 重启时从积压的消息开始重新处理 |
| 恢复时 | 执行 `docker-compose restart anomaly-detector` 后自动开始处理积压消息 |
### 场景 3 — MQTT broker (Mosquitto) 重启
| 步骤 | 行为 |
|------|------|
| 发生故障 | Mosquitto 容器重启 |
| Spring Boot | 通过 `MqttPahoMessageDrivenChannelAdapter` 的 `automaticReconnect=true` 设置尝试自动重连 |
| 模拟器 | 通过 `paho-mqtt` 的重连逻辑自动重新订阅 |
| 重连耗时 | 基于 `connectionTimeout=10s`, `keepAliveInterval=60s`,在几秒内恢复 |
| 重连期间数据 | 断开连接期间模拟器发布的消息将丢失 (基于 QoS 1,因为是 broker 重启所以无法恢复会话) |
### 场景 4 — InfluxDB 写入失败
| 步骤 | 行为 |
|------|------|
| 发生故障 | InfluxDB 无法响应或写入超时 |
| 行为 | `TelemetryRepository.save()` 抛出异常 → 向上抛出 `RuntimeException` |
| Kafka offset | 发生异常时不提交该消息的 offset → Kafka 会尝试重新处理 |
| 日志 | 记录 ERROR 级别日志 `[InfluxDB] 쓰기 실패 — vehicle={} timestamp={}` |
| 未实现限制 | 反复失败的消息可能会被无限重新处理。需要引入 DLQ 和重试次数限制 |
### 场景 5 — Redis 宕机 (Rate Limiting / BruteForce)
| 步骤 | 行为 |
|------|------|
| 发生故障 | 无法连接 Redis |
| Rate Limiting | `redisTemplate.opsForValue().increment()` 抛出异常 → 请求在 `preHandle()` 中爆开 |
| 影响范围 | Rate Limiting 和 BruteForce 检测不是被禁用,而是整个 API 返回 500 响应。因为 Refresh Token (Phase 7) 也存储在 Redis 中,所以重新登录 (登录本身不受影响,仅无法重新发放) 也会一并受到影响 |
| 运维改进方向 | 考虑添加 try-catch 以在 Redis 故障时绕过 Rate Limiting (可用性与安全性的权衡) |
## 异常检测规则 (基于 Phase 3)
| 项目 | 异常条件 |
|------|----------|
| 发动机温度 | 超过 105°C |
| RPM | 超过 6000 |
| 电池电压 | 低于 11.5V 或超过 15V |
| 速度 | 超过 200km/h |
| DTC 代码 | 数组不为空时 |
## 运行测试
```
# Java (JUnit 5)
cd backend
./gradlew test
# Python — 异常检测规则测试
cd anomaly-detector
pip install -r requirements.txt pytest
pytest
# Python — 模拟器测试
cd simulator
pip install -r requirements.txt pytest
pytest
```
## 运行方法
```
# 1. 环境变量设置
cp .env.example .env
# 编辑 .env 文件(GEMINI_API_KEY 为可选 — 若无此项则仅 AI 诊断功能无法运行,其余正常运行)
# 2. 运行完整 stack
docker-compose up -d
# 3. 运行模拟器
cd simulator
python vehicle_simulator.py
```
## OBD-II 实际连接 (ELM327 dongle)
```
pip install obd
# 将 dongle 连接到车辆 OBD-II 端口后:
import obd
connection = obd.OBD()
response = connection.query(obd.commands.SPEED)
print(response.value) # 예: 87 kph
```
## 开发原则
- 安全第一:所有通信使用 TLS,禁止无身份验证的 endpoint
- 环境变量通过 `.env` 分离,禁止硬编码
- 分离接口以便于轻松切换模拟器 ↔ 实际 OBD-II
- 测试:JUnit 5 (Java), pytest (Python)
## 文档
| 文档 | 内容 |
|------|------|
| [架构决策记录 (ADR)](docs/architecture-decisions.md) | 技术选择的原因 — 不是“用了什么”,而是“为什么选择这个” |
| [数据库 schema](docs/db-schema.md) | PostgreSQL (元数据/异常历史) + InfluxDB (时序) schema |
| [部署指南](docs/deployment-guide.md) | AWS EC2 部署流程 |
| [安全自检报告](docs/security-report.md) | 基于 OWASP Top 10, UN R155 / ISO SAE 21434 的检查结果 |
| [开发日志](docs/devlog.md) | 按日期记录的工作内容、决策事项及遇到的瓶颈 |
## 参考资源
- [MQTT 协议](https://mqtt.org)
- [Apache Kafka 官方文档](https://kafka.apache.org/documentation)
- [python-OBD](https://python-obd.readthedocs.io)
- [InfluxDB 入门](https://docs.influxdata.com)
- [UN R155 / ISO SAE 21434 汽车网络安全法规]
标签:Spring Boot, 域名枚举, 异常检测, 搜索引擎查询, 数据流处理, 时序数据, 测试用例, 版权保护, 物联网, 自定义请求头, 车联网, 软件成分分析, 逆向工具, 遥测监控