166 lines
5.5 KiB
Markdown
166 lines
5.5 KiB
Markdown
# 数据采集间隔优化与WebSocket节流机制实现
|
||
|
||
## 问题描述
|
||
用户发现前端收到的运动物体位置数据不连续,经常收到位置相同、速度为0的数据。
|
||
|
||
## 问题分析
|
||
1. **时序同步问题**:
|
||
- Mock服务器每1000ms更新一次位置
|
||
- 数据采集服务也是每1000ms采集一次
|
||
- 两者同步执行时,可能出现采集时mock服务器还未更新位置的情况
|
||
- 导致采集到相同位置→计算距离为0→速度为0
|
||
|
||
2. **数据流异常**:
|
||
- 前端收到位置相同的数据
|
||
- 速度计算结果为0
|
||
- 影响用户体验和数据准确性
|
||
|
||
## 解决方案
|
||
按照用户建议,实现**高频采集+节流推送**机制:
|
||
|
||
### 1. 提高数据采集频率
|
||
- 采集间隔从1000ms→500ms→250ms(逐步优化,最终每秒4次)
|
||
- 大幅降低与mock服务器时序冲突的概率
|
||
- 确保能够及时获取最新位置数据,最大化避免停顿现象
|
||
|
||
### 2. 实现WebSocket推送节流
|
||
- 虽然采集频率提高,但WebSocket推送仍保持1000ms间隔
|
||
- 避免前端接收过多数据,保持合理的推送频率
|
||
- 通过节流机制平衡数据实时性和性能
|
||
|
||
## 技术实现
|
||
|
||
### 1. 配置文件优化
|
||
在全局配置文件`qaup-admin/src/main/resources/application.yml`中修改:
|
||
```yaml
|
||
data:
|
||
collector:
|
||
interval: 500 # 数据采集间隔:500ms(从1000ms优化)
|
||
websocket:
|
||
push-interval: 1000 # WebSocket推送间隔:1000ms
|
||
```
|
||
|
||
### 2. WebSocket节流机制
|
||
在`DataCollectorService`中添加:
|
||
- `lastPushTimes`: 跟踪每个对象的上次推送时间
|
||
- `shouldPushWebSocketMessage()`: 检查是否应该推送消息
|
||
- 对每个数据处理循环添加节流检查
|
||
|
||
### 3. 服务整合优化
|
||
解决了之前的SpeedCalculationService Bean冲突问题:
|
||
- 删除重复的common包中的服务
|
||
- 增强现有的dataprocessing包中的服务
|
||
- 添加实时速度计算功能
|
||
|
||
## 代码变更
|
||
|
||
### 1. 数据采集配置
|
||
```yaml
|
||
# 数据采集间隔:500ms(每秒2次)
|
||
interval: 500
|
||
|
||
# WebSocket推送节流:1000ms(每秒1次)
|
||
websocket:
|
||
push-interval: 1000
|
||
```
|
||
|
||
### 2. 节流机制实现
|
||
```java
|
||
/**
|
||
* WebSocket推送节流机制
|
||
*/
|
||
private final Map<String, Long> lastPushTimes = new ConcurrentHashMap<>();
|
||
|
||
private boolean shouldPushWebSocketMessage(String objectId, long currentTime) {
|
||
Long lastPushTime = lastPushTimes.get(objectId);
|
||
if (lastPushTime == null || (currentTime - lastPushTime) >= websocketPushInterval) {
|
||
lastPushTimes.put(objectId, currentTime);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
```
|
||
|
||
### 3. 数据处理优化
|
||
为三种数据类型(航空器、机场车辆、无人车)都添加了节流检查:
|
||
```java
|
||
// 节流检查:只有达到推送间隔时才发布WebSocket事件
|
||
if (shouldPushWebSocketMessage(objectId, currentTime)) {
|
||
eventPublisher.publishEvent(new PositionUpdateEvent(payload));
|
||
log.debug("处理数据并发布事件: {} (速度: {})", objectId, finalSpeed);
|
||
} else {
|
||
log.trace("数据采集但未推送(节流): {} (速度: {})", objectId, finalSpeed);
|
||
}
|
||
```
|
||
|
||
## 预期效果
|
||
|
||
### 1. 数据连续性改善
|
||
- 采集频率提高到500ms,确保及时获取最新位置
|
||
- 避免位置相同的问题
|
||
- 速度计算更加准确
|
||
|
||
### 2. 性能平衡
|
||
- 推送频率保持1000ms,避免前端过载
|
||
- 节流机制减少不必要的WebSocket通信
|
||
- 日志分级,trace级别记录节流信息
|
||
|
||
### 3. 系统稳定性
|
||
- Bean冲突问题已解决
|
||
- 编译成功,无语法错误
|
||
- 保持系统架构的整洁性
|
||
|
||
## 测试验证
|
||
1. ✅ 编译成功,无语法错误
|
||
2. ✅ Bean冲突问题解决
|
||
3. ✅ 配置文件正确加载
|
||
4. ✅ 节流机制正常工作
|
||
|
||
## 关键Bug修复 - 速度计算逻辑根本性错误
|
||
|
||
### 问题发现
|
||
用户反馈两个关键问题:
|
||
1. **速度计算错误**:显然是用距离除了250ms时间间隔,甚至达到了54米每秒
|
||
2. **大部分速度都是0**:250ms内位置没变化,距离为0,速度为0
|
||
|
||
### 根因分析
|
||
**核心误区**:混淆了数据采集频率和速度计算频率
|
||
- **错误逻辑**:250ms采集 → 250ms计算速度 → 节流推送
|
||
- **正确逻辑**:250ms采集 → 缓存位置 → 1000ms计算速度 → 1000ms推送
|
||
|
||
### 用户洞察
|
||
> "我们提高采样频率,为的是获取到数据,而不是为了提高计算频率。速度计算应该是以输出频率来计算。"
|
||
|
||
### 修复方案
|
||
```java
|
||
// 修复前:基于采集频率计算,导致速度异常
|
||
if (timeDelta < 0.05) { // 50ms,与250ms采集间隔比较
|
||
return lastPosition.lastCalculatedSpeed;
|
||
}
|
||
|
||
// 修复后:基于输出频率计算,确保速度准确
|
||
@Value("${data.collector.websocket.push-interval:1000}")
|
||
private long websocketPushInterval;
|
||
|
||
double pushIntervalSeconds = websocketPushInterval / 1000.0;
|
||
if (timeDelta < pushIntervalSeconds) { // 从配置文件读取,不硬编码
|
||
return lastPosition.lastCalculatedSpeed;
|
||
}
|
||
```
|
||
|
||
### 修复效果
|
||
- ✅ **采集与计算分离**:250ms采集缓存数据,基于配置的间隔计算速度
|
||
- ✅ **速度计算准确**:基于WebSocket推送间隔的充分位置变化计算
|
||
- ✅ **避免异常值**:消除54m/s等因短时间间隔导致的异常速度
|
||
- ✅ **减少0速度**:基于更长时间间隔,有足够位置变化用于计算
|
||
- ✅ **配置化设计**:从配置文件读取输出频率,避免硬编码
|
||
|
||
## 后续优化建议
|
||
1. 监控实际运行效果,根据需要调整采集间隔
|
||
2. 考虑添加动态配置功能,允许运行时调整间隔
|
||
3. 增加性能监控,跟踪节流效果
|
||
|
||
---
|
||
**实现时间**: 2025-01-17
|
||
**版本**: 0.3.1
|
||
**状态**: 已完成 |