引言
浏览器前端不直接连区块链,而是连「RPC 节点」——节点像「区块链的网关」,把状态读取、交易提交、事件查询翻译成标准 JSON-RPC。而区块浏览器(Etherscan 等)则是「搜索引擎 + 数据库」,把区块和交易落库、解码、索引,让人能按地址/哈希/事件检索。这两层基础设施决定了 DApp 的可靠性、成本与可扩展性——本文将从上到下把它们讲透。
前置:https://plumephp.com/blockchain-ethereum-evm-state/(账户模型与状态 Trie)、https://plumephp.com/blockchain-transaction-lifecycle-gas-market/(交易生命周期)。
目录
- 1. JSON-RPC 协议全貌
- 2. 常用读取调用
- 3. eth_getLogs 深度解析
- 4. 写入调用与交易提交
- 5. 全节点与归档节点
- 6. 节点部署架构
- 7. 托管 RPC 与自建节点选型
- 8. 区块浏览器数据链路
- 9. 可靠性工程与错误处理
- 10. 速查表与一句话记忆
- 延伸阅读
1. JSON-RPC 协议全貌
以太坊的公开接口是 HTTP/WebSocket 上的 JSON-RPC 2.0。请求由 method + params 组成,命名空间划分清晰:
| 命名空间 | 代表方法 | 用途 |
|---|---|---|
eth_ | eth_getBalance、eth_call、eth_sendRawTransaction | 链上状态与交易 |
net_ | net_version、net_peerCount | 网络信息 |
web3_ | web3_clientVersion | 客户端信息 |
debug_ | debug_traceTransaction | 深度调试(需开启) |
txpool_ | txpool_content | 内存池查看(需开启) |
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_getBalance",
"params": ["0x...", "latest"]
}
关键设计点:
latest/pending/earliest参数:指定状态快照;latest是最新已打包区块的状态,pending包含内存池未打包交易;eth_call不改变状态:纯模拟执行,返回结果或 revert 原因,是「只读」与「预估」的主力;- 批次请求(batch):一条 HTTP 连接可发送多条请求数组,显著降低延迟与限流压力。
2. 常用读取调用
真实业务里 80% 的调用集中在少数方法上:
| 方法 | 返回 | 典型场景 |
|---|---|---|
eth_getBalance | WEI 余额 | 钱包显示、余额校验 |
eth_getTransactionByHash | 交易对象 | 交易详情、状态轮询 |
eth_getTransactionReceipt | 回执(含 logs、status) | 确认成功/失败、解析事件 |
eth_getBlockByNumber | 区块(含 tx 列表) | 区块浏览器、批量同步 |
eth_getCode | 合约字节码 | 验证合约是否已部署 |
eth_call | 函数返回 | 只读调用、模拟 |
eth_estimateGas | gas 用量 | 预估交易费用 |
eth_feeHistory | base fee 历史 | Gas 动态定价 |
eth_getLogs | 事件日志 | 事件驱动业务(见下节) |
重要反直觉点:eth_getTransactionByHash 只查「已知的交易」——如果交易还没被广播到该节点,返回 null 会被误判为「不存在」。轮询交易状态应该先查 receipt,receipt 为空说明尚未确认或尚未进入该节点视野。
3. eth_getLogs 深度解析
eth_getLogs 是「事件驱动架构」的命脉。合约 emit Event(...) 产生的日志由三部分构成:地址(address)、主题(topics)、数据(data)。topics[0] 是事件签名哈希,后续 topics 是 indexed 参数。
{
"method": "eth_getLogs",
"params": [{
"fromBlock": "0x1000000",
"toBlock": "0x10000FF",
"address": "0xTokenContract",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
]
}]
}
工程要点:
- topics 过滤:topics[0] 精确匹配事件,topics[1..] 可过滤 indexed 参数值(地址/数值);
null表示通配; - 范围限制:一次查询的区块跨度与结果量都有限制(如最多 10,000 条或 5000 区块),超出需分页或改用跟踪型索引;
- 重放与缺口:日志只在区块里,节点不会保留历史日志的「应用级游标」——业务必须自己记录
lastProcessedBlock,断点续跑; - 替代方案:高频事件追踪应走 filter(WebSocket 订阅) 或专门的索引服务(见第 8 节),而不是反复全量
getLogs。
4. 写入调用与交易提交
提交交易有两个入口:
eth_sendTransaction → 节点持有私钥,节点代签(仅本地节点/开发环境)
eth_sendRawTransaction → 客户端签名,节点只做广播(生产推荐)
生产环境一律用 eth_sendRawTransaction:私钥永不离开客户端。提交后:
- 节点校验 → 入池 → 广播;
- 用
eth_getTransactionReceipt轮询回执,status: "0x1"成功、"0x0"失败(gas 消耗但仍收手续费); - revert 需要查
eth_call或debug_traceTransaction拿 revert 原因字符串。
限流与重试:公共 RPC 对单 key 有每秒请求数(RPS)限制,超限返回 429。重试策略要用指数退避 + 抖动,而不是固定间隔狂刷——否则雪上加霜。
5. 全节点与归档节点
「节点」之间最关键的区别在于历史状态是否完整可查:
| 维度 | 全节点(Full Node) | 归档节点(Archive Node) |
|---|---|---|
| 状态存储 | 只保留最新状态快照 | 保留每个区块的历史状态 |
| 磁盘占用 | 约 1~2 TB | 约 5~12 TB(持续增长) |
| 历史查询 | 只能查 latest 附近 | 可查任意历史区块的状态 |
| 场景 | 交易广播、验证、区块同步 | 历史分析、回溯型 DApp、审计 |
eth_getBalance(addr, "0x1000000") // 归档节点:返回那个区块时的余额
// 全节点:报错或返回最新值
工程选型:只做交易提交与事件追踪 → 全节点足够;做链上历史数据分析、或用户可回溯任意时点状态的 DApp → 必须归档节点(或依赖托管归档服务)。
6. 节点部署架构
自建一个「够用的生产节点」涉及两条链:
- 执行层客户端(EL):geth / Nethermind / Erigon,处理交易执行、mempool、状态;
- 共识层客户端(CL):Prysm / Lighthouse / Lodestar,处理 PoS 共识、验证者职责。
┌─────────────┐ ┌─────────────┐
用户 ──► │ RPC 网关 │ ──► │ 执行层 geth │ ──► P2P 网络
│ (nginx/多节点│ └──────┬──────┘
└─────────────┘ │
▼
┌─────────────┐
│ 共识层 Prysm │ ──► 信标链网络
└─────────────┘
运维要点:
- 读写分离:多个执行节点 + 网关负载均衡,写请求走主节点、读走副本;
- 同步方式:
snap sync首次同步(小时级),日常--syncmode snap; - 监控:
eth_syncing轮询同步状态、net_peerCount、磁盘/内存告警; - 网络:开放 TCP 30303(EL P2P)、UDP 9000(CL discovery);关闭公开 debug/txpool 端口,避免被滥用。
7. 托管 RPC 与自建节点选型
公共基础设施提供商把「跑节点」变成「调 API」:
| 维度 | 托管(Infura/Alchemy/QuickNode) | 自建节点 |
|---|---|---|
| 上手成本 | 低,注册即用 | 高,需运维 |
| 控制力 | 限流、隐私受平台约束 | 完全自主 |
| 可靠性 | 平台 SLA,多地域 | 取决于自身高可用 |
| 成本 | 免费层 + 按量计费 | 硬件 + 运维人力 |
| 调试能力 | 受限(无 debug 接口) | 完全开放 |
混合策略(推荐):主 RPC 用托管(省心),自建一个归档/调试节点兜底,并对托管故障做 failover。注意:
- 私钥管理:托管节点永远不持私钥,签名在客户端完成;
- 多 provider 冗余:接入两家以上,故障时自动切换;
- 鉴权:API key 放服务端,绝不进前端 bundle(会被抓取盗刷)。
8. 区块浏览器数据链路
区块浏览器(Etherscan/Blockscout)本质是一个「链上数据的搜索引擎」。它的数据链路分四层:
区块/交易 ──► 同步器(并行追块) ──► 数据库(Postgres 落库)
│ │
▼ ▼
事件解码器(ABI 解析) 索引服务(地址/哈希/主题)
│ │
▼ ▼
Web API(搜索/分页) ◄── 前端(地址页/交易页/合约页)
设计难点:
- 追块与落库的一致性:同步器要处理 reorg(区块回滚),需按「区块号倒序校验 + 标记已回滚行」,防止孤儿数据;
- 事件解码:每个合约地址绑定其 ABI,
getLogs原始数据按 topics/data 解码成可读字段(transfer 的 from/to/amount); - 地址搜索:建立「地址 ↔ 交易/事件」的多级索引(B-tree / 位图),支撑毫秒级反查;
- 合约验证与源码:源码验证(flatten + 编译 + 字节码比对)让浏览器能展示可读函数调用。
9. 可靠性工程与错误处理
把「调 RPC」做成健壮工程,需要覆盖三类失败:
| 失败类型 | 症状 | 对策 |
|---|---|---|
| 瞬态(网络抖动、限流) | timeout、429 | 重试 + 指数退避 + 抖动 |
| 结构性(方法不支持、参数错) | 4xx、-32601 | 立即失败,不重试 |
| 一致性(节点落后、reorg) | 结果前后不一致 | 用 latest + 最终性判断,处理回滚 |
关键工程实践:
- idempotent 轮询:交易状态轮询做成幂等——记录
lastProcessedReceipt,重复收到同一回执不重复记账; - 最终性等待:大额/敏感交易等 12+ 确认再对外确认,避免 reorg 造成的「假成功」;
- 健康检查:定期
eth_blockNumber对比多个 provider,落后超过阈值自动切换; - 可观测性:记录每次调用的 method、耗时、错误码,形成 SLA 报表——没有指标就没有优化。
10. 速查表与一句话记忆
| 问题 | 一句话答案 |
|---|---|
| 只读调用用什么 | eth_call(模拟)与 eth_getBalance 等 get 系 |
| 读事件用什么 | eth_getLogs(范围+topics 过滤)或 WS 订阅 |
| 提交交易用什么 | eth_sendRawTransaction(客户端签名) |
| 全节点够不够 | 只做交易/事件追踪够;历史回溯要归档节点 |
| 托管还是自建 | 托管省心 + 自建兜底,混合最稳 |
| 浏览器数据从哪来 | 同步器追块 → 落库 → ABI 解码 → 索引 → API |
一句话记忆:RPC 是「读接口 + 写接口」的网关(eth_call 读、sendRawTransaction 写、getLogs 读事件),节点分全量与归档,浏览器则是把区块/交易/事件落库成可搜索数据库的索引工程。
延伸阅读
- https://plumephp.com/blockchain-ethereum-evm-state/ — 账户模型、状态 Trie 与 Gas 机制
- https://plumephp.com/blockchain-transaction-lifecycle-gas-market/ — 交易从签名到确认的全旅程
- https://plumephp.com/blockchain-onchain-data-indexing/ — The Graph/Subgraph 的事件索引范式
- https://plumephp.com/blockchain-consensus-deep-dive/ — 共识与最终性判断
- https://plumephp.com/blockchain-layer2-rollups/ — L2 的 RPC 差异与桥接
- 数据库专题 — 区块浏览器的存储与索引设计
- DevOps 专题 — 节点监控、告警与高可用运维
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。