# 实时数据接收与缓存优化方案 ## 一、当前系统问题分析 通过对前端代码的分析,我发现当前系统存在以下问题: 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.title}

${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的浏览器 - 实现降级方案 通过以上方案的实施,预计可以显著提高系统的性能和用户体验,减少服务器压力,实现真正的实时数据更新。