web3 sendTransaction调用在Ubuntu上CPU负载过高,Windows无此问题
Web3 发送交易Ubuntu环境CPU占满、资源泄漏解决方案
核心问题根因
- 未主动销毁Provider底层资源:仅将web3实例赋值为null无法触发底层HTTPProvider的资源回收,绑定的事件监听器会持续持有provider引用,导致GC无法回收socket连接,大量残留连接堆积引发epoll高频轮询占用CPU。
- sendTransaction轮询逻辑未终止:你仅在receipt、error回调中修改标志位跳出了业务侧的等待循环,但web3内部监听confirmation的默认逻辑会持续查询区块确认(默认查满24个确认才停止),后台请求没有被主动中断。
- 跨平台TCP行为差异:Windows默认的TCP超时回收阈值远短于Ubuntu,你在Windows侧看到的
Failed to check for transaction receipt报错就是超时后系统主动断开连接触发了error事件销毁了资源,而Ubuntu侧TCP超时时间长,pm2托管的进程还会忽略部分连接断开信号,导致error事件没有触发,资源泄漏持续累积。 - 频繁创建销毁web3实例加剧泄漏:每分钟新建一个web3实例的逻辑本身就会放大资源泄漏的影响。
修复方案
1. 主动销毁Provider连接
不要仅将web3实例设为null,执行完交易逻辑后主动断开底层provider连接,替换原有的web3销毁逻辑:
if (web3T1 != null) { // 先断开provider连接,释放socket if(web3T1.currentProvider?.disconnect) { web3T1.currentProvider.disconnect() } // 兼容旧版本web3的销毁逻辑 else if(web3T1.currentProvider?.requestManager?.provider?.destroy) { web3T1.currentProvider.requestManager.provider.destroy() } web3T1 = null logger.debug(`provider closed.`) }
2. 优化sendTransaction监听逻辑,避免后台无效轮询
不要用自定义标志位死等结果,直接用await等待交易执行完成,同时主动限制确认数、及时移除事件监听:
try { let confirmedOrReverted = false const receipt = await web3T1.eth.sendTransaction(txData) .once('transactionHash', hash => logger.info(`recieved transaction hash: ${hash}`)) .once('receipt', receipt => { logger.info(`Tx receipt => ${receipt.status === true? 'success' : 'failed'}!`) confirmedOrReverted = true }) .on('confirmation', (confirmationNumber, receipt) => { logger.info(`recieved confirmation #${confirmationNumber}`); // 达到需要的确认数后主动移除所有监听,终止内部轮询 if(confirmationNumber >= 1) { // 可根据需要调整确认数阈值 this.removeAllListeners() } }) .once('error', error => { if (error.message.includes('Transaction has been reverted by the EVM')) { logger.info(`Transaction has been reverted by the EVM.`) } else { logger.error(`TX exception: ${error.message} `) } confirmedOrReverted = true }) // 移除原有while死等逻辑,await会自动等待交易处理完成 } catch(err) { logger.error(`error: ${err}`) }
3. 给HTTP provider加socket强制销毁配置
修改httpOptions,传入自定义HTTP Agent强制超时销毁socket:
const http = require('http') let httpOptions = { keepAlive: false, timeout: 40000, // ms agent: new http.Agent({ keepAlive: false, maxSockets: 1, timeout: 40000, destroyTimeout: 1000 // 超时后1秒强制销毁socket }) }
4. 复用全局web3实例
无需每分钟新建web3实例,全局初始化一次即可,避免频繁创建销毁实例带来的额外资源开销。
内容的提问来源于stack exchange,提问作者flo
相关产品推荐
相关产品推荐

