eRPC智能批处理:1000个并发请求如何变成1次上游调用
【免费下载链接】erpceRPC — fault-tolerant evm rpc proxy项目地址: https://gitcode.com/gh_mirrors/erpc1/erpc
eRPC 是一款高容错(fault-tolerant)的 EVM/Solana RPC 代理,它的核心杀手锏之一就是智能批处理:当 1000 个客户端在同一瞬间发出相同的请求时,eRPC 会自动把它们折叠成1 次真实的上游调用,每个调用者都能拿到属于自己的响应副本。本文将带你完整搞懂这套批处理 + 请求复用(multiplexing)机制是如何工作的,以及如何用最少的配置让它为你省钱。
高并发 RPC 的三个隐形成本
如果你的节点上跑着钱包、行情看板、区块轮询器,你会发现一个残酷的事实:
- 重复请求:1000 个浏览器标签页同时轮询
eth_blockNumber,上游节点被同一份数据打 1000 次; - HTTP 开销:每条调用都要独立建连、握手、序列化、限流计费;
- 限流配额:按请求计费的供应商(Alchemy、QuickNode 等)会在流量尖峰时直接烧穿你的预算。
eRPC 用三层互相独立的批处理机制,把这三笔开销全部砍掉:
上图是 eRPC 官方模拟器界面,可以直观看到流量从 Client 进入 eRPC 路由层、再分发到多个上游(Upstream)的完整链路——批处理就发生在这一层。
第一层:入站批处理 —— 一次 HTTP 请求并行执行
如果你给 eRPC 发送的是一个 JSON 数组(JSON-RPC 批处理格式),它会:
- 检测到请求体首字节是
[,立即识别为批处理; - 把数组拆成 N 个子请求,每个子请求在独立 goroutine 中并行执行;
- 任何一个子请求失败(甚至 panic),都只影响数组里对应的那一个位置,其余照常返回。
对调用方来说,钱包只需一次fetch就能同时拿到余额、nonce 和链 ID。这一层完全自动,无需任何配置。
📌 实现细节:erpc/http_server.go 中的批处理检测与并行分发,erpc/http_batch_resp.go 中的BatchResponseWriter以流式方式逐条写出响应数组,整个响应从不完整缓存在内存中,千条批处理也不会撑爆内存。
第二层:出站重组批 —— N 条调用合并成 1 个 POST
更狠的是反方向:eRPC 可以把来自不同客户端的 N 条独立调用,重新打包成一个 JSON-RPC 数组,向支持批处理的上游只发 1 次 HTTP POST。
启用只需在上游配置里加 3 个参数:
| 参数 | 作用 | 建议值 |
|---|---|---|
jsonRpc.supportsBatch: true | 打开出站批处理(默认关闭) | true |
jsonRpc.batchMaxSize: 100 | 攒够多少条立即发送 | 100~200 |
jsonRpc.batchMaxWait: 50ms | 兜底定时器:未满也强制发送 | 50ms |
⚠️避坑指南:batchMaxSize和batchMaxWait必须同时设置非零值——只设一个,另一个会退化成"每条立即刷出",批处理形同虚设。另外,如果你的调用方总是复用同一个 JSON-RPCid(比如永远传 1),会触发提前刷出,浪费攒批效果。
📌 攒批队列与定时器刷新的完整实现见 clients/http_json_rpc_client.go,字段定义在 common/config.go。
第三层:在途请求复用 —— 1000 个并发变 1 次上游调用
这是标题中魔法的来源:Multiplexer(请求复用器)。
当 1000 个并发请求"方法相同、参数相同"(哪怕 JSON-RPCid不同)同时到达时,eRPC 会:
- 算指纹:用
方法名 + 参数 SHA-256生成去重哈希,id不参与哈希,所以 id 不同的相同请求照样能合并; - 选主:第一个注册该哈希的请求成为Leader,正常走缓存查询和上游调用;其余 999 个成为Follower,只是挂在一个等待通道上;
- 分副本:Leader 拿到结果后,每个 Follower 深拷贝一份响应,并把
id改回自己请求的原始 id——调用方完全感知不到自己被"合并"了; - 安全清理:Leader 结束后先删除哈希表项、再等所有 Follower 拷贝完成,才释放响应内存,杜绝 use-after-free。
📌 核心实现:erpc/multiplexer.go(Leader 关闭与副本克隆)、erpc/networks.go(handleMultiplexing选主、waitForMultiplexResult等待、cleanupMultiplexer清理)。官方压测 erpc/networks_multiplexer_test.go 验证了 10 并发、50 并发、3 波×10 并发下每批恰好只有 1 次上游调用。
💡 注意 Solana(SVM)链的特殊处理:base58 地址是大小写敏感的,eRPC 为 SVM 单独派生了去重键,避免仅大小写不同的两个账户被错误合并——这是很多"简单哈希去重"方案会踩的坑。
1000 个并发请求,上游到底收到几次调用?
假设 1000 个看板同时轮询eth_blockNumber,且你的配置是"入站自动批处理 + 出站攒批 + 复用开启",一次流量尖峰的处理过程是:
| 阶段 | 发生了什么 | 上游视角 |
|---|---|---|
| ① 入站 | 数组请求被并行拆开,各自计算去重哈希 | 0 次 |
| ② 复用 | 999 个 Follower 挂到 Leader 的等待通道 | 0 次 |
| ③ 缓存 | Leader 命中本地/共享缓存 | 0 次 |
| ④ 兜底 | 若缓存未命中,Leader 发起唯一一次上游调用 | 1 次 |
| ⑤ 返回 | 1000 个调用方各拿到id正确的独立响应 | — |
官方文档把这层机制称为"in-flight deduplication: N concurrent identical calls → 1 upstream round-trip"。完整说明可参考 docs/pages/operation/batch.mdx。
如何验证批处理真的在省钱
eRPC 内置了 Prometheus 指标,最直接的一个是:
erpc_network_multiplexed_request_total—— 每有 1 个请求被合并去重就 +1(Leader 本身不计入)
这个计数器的值,就是被省掉的真实上游调用次数。你可以在监控面板里对比"接收请求速率"与"上游请求速率"两条曲线,差值越大,批处理省得越多:
配套的大盘配置(Grafana 仪表盘 + Prometheus 告警规则)都在仓库的 monitoring/ 目录下,直接拷走就能用。
开启建议:谁该开,谁该关
✅保持开启(复用默认开启,缺省即 ON):
- 行情看板、钱包前端、区块高度轮询——大量"热点相同读取",收益最大;
- 出站攒批适合每秒数百条
eth_getLogs的索引器,把 HTTP 开销和按次计费压到最低。
❌建议关闭(multiplexing: false):
- 每条请求参数都唯一的索引任务——去重几乎不命中,反而多付锁竞争;
- 排查问题时想逐条追踪上游调用;
- 依赖非幂等方法且 eRPC 无法可靠判重的场景。
小技巧:用networkDefaults.multiplexing一次性为项目内所有网络设置默认值,再只对"热点网络"单独覆盖,配置最干净。
一句话总结
eRPC 的智能批处理 =入站并行拆分 + 出站攒批重组 + 在途请求复用三层组合拳:让 1000 个并发相同请求只产生 1 次上游调用,让 100 条独立调用共享 1 个 HTTP POST。对按量计费的 RPC 用户来说,这不只是性能优化,而是真金白银的成本削减——而且除 3 个可选参数外,几乎零配置即可生效。
【免费下载链接】erpceRPC — fault-tolerant evm rpc proxy项目地址: https://gitcode.com/gh_mirrors/erpc1/erpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考