加密货币实时价格获取致服务器内存占满的优化方案问询
实时加密货币价格获取服务内存占用过高的优化方案
问题背景
我开发了一套实时获取加密货币价格的服务:从CoinGecko API获取大部分代币价格,针对BTC、ETH、XRP则通过PancakeSwap工厂合约+WebSocket ticker获取。因交易平台需求,服务需要每6秒执行一次价格拉取,但运行一段时间后服务器内存会被占满。之前代码的时间复杂度是O(n²),优化后内存占用有所下降,但仍需进一步优化。想请教:
- 是否需要在服务器端做主动内存清理?
- 当前代码层面还有哪些可优化的点?
优化后代码片段
// Process each token periodically const processTokens = async () => { try { for (const token of TOKENS) { const priceData = await liquidityContract.methods .getPrice([token.address, token.address]) .call(); const _openprice = parseFloat(priceData.price) / 10 ** 8; if (["ETH", "BTC", "XRP"].includes(token.symbol)) { // Special handling for ETH, BTC, and XRP client.ws.ticker(token.symbol + "USDT", (ticker) => { feed = formatFeedFromTicker(ticker, _openprice); updatePriceFeed(token, feed, callsendprice, tokenDatas); }); } else { // General handling for other tokens if ( !apiCache[token.coinid] || Date.now() - apiCache[token.coinid].timestamp > CACHE_DURATION ) { const response = await fetch( `https://pro-api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${token.coinid}&order=market_cap_desc&per_page=1&page=1&sparkline=false&x_cg_pro_api_key=${config.CG_KEY}` ); if (!response.ok) continue; const data = await response.json(); if (data.length === 0) continue; apiCache[token.coinid] = { data: data[0], timestamp: Date.now(), }; } const feed = formatFeedFromAPI( _openprice, apiCache[token.coinid].data ); updatePriceFeed(token, feed, callsendprice, tokenDatas); } } if (broadcastMessage) { broadcastMessage({ tokenData: tokenDatas }); } } catch (error) { console.error("Error in processTokens: ", error); } finally { // Schedule the next run setTimeout(processTokens, 2000); } }; // Start the process processTokens(); // Transaction checking if (calltx) { monitorTransactions(callbacktx); } };
核心优化方案
一、代码层面优化点
1. 修复WebSocket订阅泄漏(关键内存泄漏点)
每次processTokens执行时,都会给BTC/ETH/XRP新增WebSocket订阅回调,但旧订阅从未取消,导致连接、回调函数持续累积,内存不断上涨。
- 修复:维护订阅映射,每次处理前取消旧订阅再创建新订阅,或复用订阅仅更新价格基准
// 全局维护订阅实例映射 const wsSubscriptions = new Map(); // 替换原BTC/ETH/XRP处理逻辑 if (["ETH", "BTC", "XRP"].includes(token.symbol)) { const symbolPair = token.symbol + "USDT"; // 清理旧订阅 if (wsSubscriptions.has(symbolPair)) { wsSubscriptions.get(symbolPair).unsubscribe(); wsSubscriptions.delete(symbolPair); } // 创建新订阅并保存实例 const subscription = client.ws.ticker(symbolPair, (ticker) => { feed = formatFeedFromTicker(ticker, _openprice); updatePriceFeed(token, feed, callsendprice, tokenDatas); }); wsSubscriptions.set(symbolPair, subscription); }
2. 避免异步任务叠加
当前用finally里的setTimeout调度下一次执行,若异步操作(API请求、合约调用)耗时超过2秒,会导致多个processTokens实例并行运行,内存堆积大量未完成的Promise和请求上下文。
- 修复:确保前一次执行完成后再调度下一次
try { // ... 原逻辑 ... // 所有异步操作完成后再调度下一次 setTimeout(processTokens, 2000); } catch (error) { console.error("Error in processTokens: ", error); // 出错时仍调度下一次,避免服务中断 setTimeout(processTokens, 2000); } // 移除finally中的setTimeout
3. 新增缓存清理机制
apiCache只添加不清理,若代币从TOKENS列表移除,对应缓存会永久占用内存。
- 优化:定期清理过期缓存
// 在processTokens开头添加清理逻辑 const now = Date.now(); for (const [coinId, cacheEntry] of Object.entries(apiCache)) { // 清理超过缓存有效期2倍的条目 if (now - cacheEntry.timestamp > CACHE_DURATION * 2) { delete apiCache[coinId]; } }
4. 并行化合约调用提升效率
原for...of循环串行执行合约调用,代币数量多时单次执行时间过长,加剧任务叠加问题。
- 优化:用
Promise.all批量并行处理合约调用
// 替换原循环中的合约调用部分 const tokenPricePromises = TOKENS.map(async (token) => { const priceData = await liquidityContract.methods.getPrice([token.address, token.address]).call(); return { token, _openprice: parseFloat(priceData.price) / 10 ** 8 }; }); const tokenPriceResults = await Promise.all(tokenPricePromises); // 遍历并行结果处理代币 for (const { token, _openprice } of tokenPriceResults) { // 原有的BTC/ETH/XRP和其他代币处理逻辑 }
二、服务器端内存清理说明
Node.js自带自动垃圾回收机制,无需手动做内存清理。内存持续上涨的核心原因是代码存在资源泄漏(如未释放的WebSocket订阅、定时器),优先解决代码层面的泄漏问题比服务器端手动清理更有效。
若需定位泄漏点,可定期用process.memoryUsage()输出内存状态,或用Chrome DevTools的堆快照工具分析内存占用情况。
内容的提问来源于stack exchange,提问作者Vinit_2311
相关产品推荐
相关产品推荐

