Trc-20转账监听最优算法咨询:批量地址HTTP监控适配Tron高并发
问题
需要监听大量地址的TRC-20代币转入交易,当交易涉及目标地址时,将交易信息存入数据库。当前无法使用WebSocket监听,只能通过HTTP请求实现。现有代码如下:
const trc20ContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; let contract = await tronWeb.contract().at(trc20ContractAddress); let result = await tronGrid.contract.getEvents(trc20ContractAddress, { only_confirmed: false, event_name: "Transfer", limit: 200, min_timestamp: timestamp, order_by: "timestamp,asc" }) console.log(result)
已知Tron网络每秒可处理2000笔交易,单请求最大返回条数为200,同时需考虑请求延迟且无法监听全区块,求最优监听算法。
最优监听算法设计
核心思路
基于时间窗口动态调整+增量拉取+内存过滤去重+并发控制的组合策略,平衡请求效率、数据完整性和系统负载。
1. 动态时间窗口适配交易密度
- 初始设置1秒基础时间窗口,根据每次请求返回的事件数量实时调整:
- 若单次请求返回200条(达上限),说明当前窗口交易密集,将窗口缩小至0.3秒(Tron每秒2000笔,0.3秒约600笔,3次请求可覆盖);
- 若连续3次请求返回条数低于50,说明交易稀疏,将窗口扩大至5秒,减少无效请求;
- 窗口范围锁死在0.3秒-5秒之间,避免过小导致请求过于频繁,或过大导致单次拉取数据过载。
2. 增量拉取+断点续传
- 维护全局
lastProcessedTimestamp变量,初始设为当前时间往前推10分钟(避免漏过服务启动前的交易),每次请求的min_timestamp直接用该值; - 每次请求完成后,将
lastProcessedTimestamp更新为返回结果中最后一条事件的时间戳; - 请求失败时采用指数退避重试(第一次等1秒,第二次2秒,最多重试5次),重试时保持原时间戳不变,防止数据丢失。
3. 高效过滤与去重
- 把所有目标地址存入内存
Set集合,拉取到Transfer事件后,直接判断to字段是否在集合中,O(1)复杂度完成过滤; - 用
transaction_id + log_index作为交易唯一标识,数据库端给该字段加唯一索引,同时在内存中维护最近1小时的交易ID缓存,双重避免重复入库; - 业务允许的话,设置
only_confirmed: true,只处理已确认交易,减少无效写入。
4. 分页拉取与并发控制
- 每次请求后检查返回结果的
has_more标识,若为真,立即发起下一页请求(递增offset参数),直到拉完当前窗口的所有数据; - 限制并发请求数不超过3个,避免触发TronGrid的API频率限制,同时保证拉取速度。
5. 异常监控与自适应调整
- 记录每次请求的耗时、返回条数、失败次数,若请求延迟超过当前窗口的1/3,临时缩小窗口,避免数据堆积;
- 连续5次请求都返回满200条时触发告警,提示交易流量远超预期,需人工介入调整。
优化后代码示例
const trc20ContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; const targetAddresses = new Set(["目标地址1", "目标地址2", "..."]); // 内存哈希集合 let lastProcessedTimestamp = Date.now() - 10 * 60 * 1000; // 初始时间:10分钟前 let timeWindow = 1000; // 初始窗口1秒 let consecutiveLowVolume = 0; async function pollTransferEvents() { try { let hasMore = true; let offset = 0; const currentEndTimestamp = Date.now(); // 拉取当前时间窗口内所有事件 while (hasMore) { const result = await tronGrid.contract.getEvents(trc20ContractAddress, { only_confirmed: true, event_name: "Transfer", limit: 200, min_timestamp: lastProcessedTimestamp, max_timestamp: currentEndTimestamp, order_by: "timestamp,asc", offset: offset }); // 过滤目标地址交易并入库 const validEvents = result.data.filter(event => targetAddresses.has(event.result.to) ); await saveToDatabase(validEvents); // 需实现带唯一索引的入库逻辑 // 更新断点与分页状态 if (result.data.length > 0) { lastProcessedTimestamp = result.data[result.data.length - 1].timestamp; } hasMore = result.has_more || false; offset += 200; // 动态调整时间窗口 if (result.data.length === 200) { consecutiveLowVolume = 0; if (timeWindow > 300) { timeWindow = Math.floor(timeWindow * 0.8); } } else { consecutiveLowVolume++; if (consecutiveLowVolume >= 3 && timeWindow < 5000) { timeWindow = Math.floor(timeWindow * 1.5); } } } // 计算下一次请求等待时间,避免频繁请求 const waitTime = Math.max(0, timeWindow - (Date.now() - currentEndTimestamp)); setTimeout(pollTransferEvents, waitTime); } catch (error) { console.error("拉取事件失败:", error); // 指数退避重试 const retryDelay = Math.min(2 ** (consecutiveLowVolume + 1) * 1000, 30000); setTimeout(pollTransferEvents, retryDelay); } } // 启动监听 pollTransferEvents();
内容的提问来源于stack exchange,提问作者vbpkzxczhbdgwrr
相关产品推荐
相关产品推荐

