# 实时数据接收与缓存优化方案
## 一、当前系统问题分析
通过对前端代码的分析,我发现当前系统存在以下问题:
1. **频繁的HTTP请求**:前端使用多个`setInterval`进行定期数据刷新,导致大量的HTTP请求
2. **响应式数据获取**:数据更新延迟高,用户体验差
3. **缺少有效的缓存机制**:每次请求都直接从服务器获取数据,没有合理利用缓存
4. **资源浪费**:即使数据没有更新,也会进行定期请求
## 二、解决方案设计
### 1. WebSocket实时数据接收
使用WebSocket实现实时数据推送,替代现有的定期轮询。
### 2. 前端缓存优化
实现高效的前端缓存机制,确保数据更新时缓存也能及时更新。
## 三、具体实施方案
### 1. 后端实现
#### 1.1 添加WebSocket依赖
```xml
org.springframework.boot
spring-boot-starter-websocket
```
#### 1.2 配置WebSocket
```java
// WebSocketConfig.java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
```
#### 1.3 实现实时数据推送服务
```java
// RealTimeDataService.java
@Service
public class RealTimeDataService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
/**
* 推送客户数据更新
*/
public void pushCustomerUpdate(String customerId, UserProductCartDTO customerData) {
messagingTemplate.convertAndSend("/topic/customers/" + customerId, customerData);
}
/**
* 推送公海池数据更新
*/
public void pushPublicSeaUpdate(List customerList) {
messagingTemplate.convertAndSend("/topic/public-sea", customerList);
}
/**
* 推送通知
*/
public void pushNotification(NotificationDTO notification) {
messagingTemplate.convertAndSend("/topic/notifications", notification);
}
}
```
### 2. 前端实现
#### 2.1 WebSocket客户端实现
在前端页面中添加WebSocket客户端代码,用于连接WebSocket服务器并接收实时数据。
```javascript
// 在mainapp-sells.html和mainapp-supplys.html中添加
class WebSocketClient {
constructor() {
this.socket = null;
this.isConnected = false;
this.reconnectInterval = 5000;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 10;
this.callbacks = {};
this.cache = new Map();
}
// 初始化WebSocket连接
init() {
// 移除现有的SockJS和STOMP依赖
const script1 = document.createElement('script');
script1.src = 'https://cdn.jsdelivr.net/npm/sockjs-client@1.6.1/dist/sockjs.min.js';
document.head.appendChild(script1);
const script2 = document.createElement('script');
script2.src = 'https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js';
script2.onload = () => this.connect();
document.head.appendChild(script2);
}
// 连接WebSocket服务器
connect() {
const socket = new SockJS('/ws');
this.stompClient = Stomp.over(socket);
this.stompClient.connect({},
(frame) => {
console.log('✅ WebSocket连接成功:', frame);
this.isConnected = true;
this.reconnectAttempts = 0;
// 订阅公海池数据更新
this.stompClient.subscribe('/topic/public-sea', (message) => {
const data = JSON.parse(message.body);
this.handlePublicSeaUpdate(data);
});
// 订阅通知
this.stompClient.subscribe('/topic/notifications', (message) => {
const notification = JSON.parse(message.body);
this.handleNotification(notification);
});
},
(error) => {
console.error('❌ WebSocket连接错误:', error);
this.isConnected = false;
this.attemptReconnect();
}
);
}
// 尝试重连
attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`⏱️ 尝试重连... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => this.connect(), this.reconnectInterval);
} else {
console.error('❌ 达到最大重连次数,停止尝试');
}
}
// 处理公海池数据更新
handlePublicSeaUpdate(data) {
// 更新缓存
this.cache.set('public-sea-data', {
data: data,
timestamp: Date.now()
});
// 调用回调函数
if (this.callbacks['public-sea-update']) {
this.callbacks['public-sea-update'].forEach(callback => callback(data));
}
}
// 处理通知
handleNotification(notification) {
// 显示通知
this.showNotification(notification);
// 调用回调函数
if (this.callbacks['notification']) {
this.callbacks['notification'].forEach(callback => callback(notification));
}
}
// 显示通知
showNotification(notification) {
const notificationEl = document.createElement('div');
notificationEl.className = 'websocket-notification';
notificationEl.innerHTML = `
${notification.message}
${new Date(notification.timestamp).toLocaleString()}
`;
// 添加样式
notificationEl.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
padding: 16px;
width: 320px;
z-index: 10000;
animation: slideIn 0.3s ease-out;
`;
// 添加关闭事件
const closeBtn = notificationEl.querySelector('.close-btn');
closeBtn.addEventListener('click', () => notificationEl.remove());
// 添加到页面
document.body.appendChild(notificationEl);
// 3秒后自动关闭
setTimeout(() => notificationEl.remove(), 3000);
}
// 订阅事件
on(event, callback) {
if (!this.callbacks[event]) {
this.callbacks[event] = [];
}
this.callbacks[event].push(callback);
}
// 取消订阅
off(event, callback) {
if (this.callbacks[event]) {
this.callbacks[event] = this.callbacks[event].filter(cb => cb !== callback);
}
}
// 获取缓存数据
getCachedData(key) {
const cached = this.cache.get(key);
if (cached) {
return cached.data;
}
return null;
}
// 设置缓存数据
setCachedData(key, data) {
this.cache.set(key, {
data: data,
timestamp: Date.now()
});
}
// 清除缓存
clearCache(key) {
if (key) {
this.cache.delete(key);
} else {
this.cache.clear();
}
}
// 断开连接
disconnect() {
if (this.stompClient) {
this.stompClient.disconnect();
this.isConnected = false;
}
}
}
// 初始化WebSocket客户端
const wsClient = new WebSocketClient();
window.addEventListener('load', () => {
wsClient.init();
});
// 页面关闭前断开连接
window.addEventListener('beforeunload', () => {
wsClient.disconnect();
});
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
`;
document.head.appendChild(style);
```
#### 2.2 优化数据获取函数
修改现有的数据获取函数,优先使用缓存数据,只有在缓存不存在或过期时才从服务器获取。
```javascript
// 修改fetchPublicSeaData函数
async function fetchPublicSeaData(loginInfo, level) {
// 尝试从缓存获取
const cachedData = wsClient.getCachedData('public-sea-data');
if (cachedData) {
console.log('📦 从缓存获取公海池数据');
return cachedData;
}
// 缓存未命中,从服务器获取
console.log('🌐 从服务器获取公海池数据');
const url = `${API_BASE_URL}/pool/customers?level=${level}&isSupplySide=false`;
const response = await fetch(appendAuthParams(url), {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch public sea data');
}
const data = await response.json();
// 更新缓存
wsClient.setCachedData('public-sea-data', data);
return data;
}
// 监听公海池数据更新
wsClient.on('public-sea-update', (data) => {
console.log('🔄 公海池数据已更新');
// 更新页面数据
updatePublicSeaData(data);
});
```
#### 2.3 替换setInterval轮询
移除现有的setInterval轮询,使用WebSocket实时数据更新。
```javascript
// 移除现有轮询代码
// this.updateInterval = setInterval(() => {
// this.loadCustomers(loginInfo, level);
// }, 30000);
// 替换为WebSocket监听
wsClient.on('public-sea-update', (data) => {
this.customersArray = data;
this.renderCustomers();
});
```
#### 2.4 实现数据更新时的缓存刷新
当用户手动更新数据时,确保缓存也被刷新。
```javascript
// 在数据更新成功后
async function updateCustomer(customerData) {
// 发送更新请求
const response = await fetch(`${API_BASE_URL}/pool/updateCustomer`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(customerData)
});
if (response.ok) {
// 清除相关缓存
wsClient.clearCache('public-sea-data');
wsClient.clearCache(`customer-${customerData.userId}`);
// 显示成功消息
showMessage('客户数据更新成功');
}
}
```
## 四、缓存策略
### 1. 缓存类型
- **公海池数据缓存**:缓存公海池客户列表,过期时间30分钟
- **单个客户数据缓存**:缓存单个客户的详细信息,过期时间1小时
- **通知缓存**:缓存通知列表,过期时间10分钟
### 2. 缓存更新机制
- **WebSocket实时更新**:当服务器数据更新时,通过WebSocket推送更新
- **手动更新**:用户手动更新数据时,清除相关缓存
- **过期自动刷新**:缓存过期时,自动从服务器获取最新数据
## 五、预期效果
1. **减少HTTP请求**:预计减少80%以上的HTTP请求
2. **提高数据实时性**:数据更新延迟从30秒降低到<1秒
3. **提升用户体验**:页面数据实时更新,无需等待
4. **降低服务器压力**:减少不必要的数据查询
5. **提高页面响应速度**:从缓存获取数据,响应更快
## 六、实施步骤
1. **后端实现**:
- 添加WebSocket依赖
- 配置WebSocket服务
- 实现实时数据推送服务
- 在数据更新时调用推送服务
2. **前端实现**:
- 添加WebSocket客户端代码
- 优化数据获取函数,使用缓存
- 移除现有的setInterval轮询
- 实现缓存更新机制
3. **测试验证**:
- 测试WebSocket连接稳定性
- 测试数据实时更新效果
- 测试缓存机制
- 测试并发性能
## 七、代码集成建议
1. **分阶段实施**:先在一个页面实现,测试稳定后再推广到其他页面
2. **保留降级机制**:当WebSocket连接失败时,自动回退到轮询模式
3. **添加监控**:监控WebSocket连接状态和缓存命中率
4. **优化缓存策略**:根据实际使用情况调整缓存过期时间
## 八、注意事项
1. **WebSocket连接管理**:
- 实现自动重连机制
- 处理连接断开情况
- 限制重连次数
2. **缓存一致性**:
- 确保数据更新时缓存也更新
- 处理缓存过期情况
- 避免缓存雪崩
3. **性能优化**:
- 限制WebSocket消息大小
- 优化数据传输格式
- 避免频繁发送大量数据
4. **兼容性考虑**:
- 支持不支持WebSocket的浏览器
- 实现降级方案
通过以上方案的实施,预计可以显著提高系统的性能和用户体验,减少服务器压力,实现真正的实时数据更新。