RPC 高可用:多 Provider、Quorum、Hedging 与缓存¶
30 秒版(开场)¶
多 RPC 轮询不等于高可用。先按 method capability、archive/trace、head/finality 和配额给 endpoint 分池;健康检查要验证 chain ID、head hash/lag 和代表性方法。确定性只读可 retry/hedge,第一条成功不一定正确;高价值读可按同一 block hash 做 quorum,但 quorum 只是分歧检测,不会创造链上共识或 finality。广播只重复发送同一 raw tx,不能 hedge 两次 “构造+签名”。缓存必须 finality-aware:按 block hash 且达到声明安全水位的结果可长缓存,
latest和 pending 只能短缓存或不缓存。
3 分钟版(精讲深度)¶
- 方法分类:immutable read、head-dependent read、mempool/write、subscription、archive/trace。
- 路由:endpoint capability + health + rate budget + region/client diversity。
- Hedging:首请求超过分位延迟后再发第二个,首个成功返回并取消其余;只用于安全幂等读。
- Quorum:统一请求 block reference,比较 hash/normalized result;分歧时 fail closed 或降级。
10 分钟版(原理 + 图示)¶
flowchart LR
Request --> Classify["method + consistency class"]
Classify --> Pool["capability pool"]
Pool --> P1["provider A"]
Pool --> P2["provider B"]
Pool --> P3["self-hosted"]
P1 --> Normalize["normalize + block evidence"]
P2 --> Normalize
P3 --> Normalize
Normalize --> Decision["first-success / quorum"]
Cache["finality-aware cache"] --> Decision
读取一致性级别
| 类别 | 示例 | 策略 |
|---|---|---|
| immutable | block by hash、在声明共识假设下 finalized 的 receipt | retry、hedge、长缓存并保留证据 |
| canonical by height | block N | 比较 hash,reorg 前可能变化 |
| head-dependent | balance at latest、gas estimate | 返回 endpoint/head evidence,短缓存 |
| pending/mempool | pending nonce、txpool | sticky/主 endpoint,结果天然局部 |
| write | send raw tx | 同 raw bytes 多播可幂等,记录每个响应 |
Quorum 不能直接比较 JSON 字符串:字段顺序、缺省值和 provider 扩展可能不同。先按协议语义 normalize,并绑定 chain ID、block hash/slot 和 commitment。 即使多个 endpoint 一致,它们也可能共享同一上游、同一 client bug 或同一陈旧快照;多数结果 不能替代轻客户端/共识证明,也不能把概率确认升级成确定 finality。
Hedging 代价
Hedge 能削尾延迟,也会增加请求量并可能同时触发 provider 限流。delay 应来自近期 latency 分布,并设全局预算;第一请求快速失败时可立即尝试下一个。
缓存
- key 包含 chain/network、method、normalized params、block reference 和 capability/version。
latest结果不能在 head 变化后继续冒充新状态。- error/empty negative cache 要短且分类型,RPC 暂时失败不能缓存成“链上不存在”。
生产场景¶
- 提现前余额/nonce:同一 sender 的 pending 视图尽量 sticky,最终由 transaction manager 持久化 reservation。
- 充值确认:按 tx/block hash 从多源验证 canonical/finality。
- trace:独立重资源池、并发限额和长 timeout,不与普通 RPC 共用。
排查与工具¶
每次响应记录 endpoint、latency、chain/head/finalized hash、cache status、hedge/quorum decision 和 normalized error。监控 provider disagreement、lag、429、method-specific success 与成本。
架构取舍¶
多 provider 提高可用性,但若都依赖同一上游或同一 client bug,会相关失败。自建节点、不同 client/region/provider 的多样性更有价值;quorum 也增加延迟和费用,应按资金风险分级使用。
深挖问答¶
- 第一条成功为何可能错? → endpoint 可能落后、在不同 fork、返回缓存或方法语义不同。
- 写请求能 hedge 吗? → 只能安全重复同一 raw tx;不能并发生成不同 nonce/payload。
- pending nonce 能 quorum 吗? → 各节点 mempool 不同,简单多数无统一真相;以本地 reservation 为主并做链上恢复。
- 何时长缓存? → 结果绑定 finalized/不可变 block hash 且方法语义确定时。
- quorum 分歧怎么办? → 保存证据、切换/隔离异常 endpoint,高价值路径 fail closed。
- 三家 provider 一致是否等于链已 finalized? → 否;它只说明这些观察源当前一致, finality 仍来自目标链共识/协议证据。
反模式与事故¶
- 只用
eth_chainId做健康检查,节点其实落后数千块。 - 对
eth_sendTransaction自动重试并重新签名。 latestbalance 缓存几分钟,用户看到过期可用余额。- Quorum 比较原始 JSON,因格式差异误判。
代码示例¶
见 pool.go:
go test -race ./examples/senior/rpcpool/...