上一篇过了一下以太坊的启动流程,这篇详细分析一下RPC的完整流程。
以太坊遵循JSON RPC规范,API列表参见以下链接:
https://github.com/ethereum/wiki/wiki/JSON-RPC
本文主要分析一下API注册和API调用的主要流程。
1. API注册流程
先看一张图,理清各组件之间的关系:
上篇提到,Node启动时会调用各Service的构造函数创建Service实例。Service是一个接口,要对外暴露RPC API的模块都需要实现该接口,比如Ethereum, Whisper, Swarm等等,在图中统一用<module>表示。
在Node的startRPC()函数中,首先会调用每个Service的APIs()函数,把所有RPC API收集到一个数组中:
// Gather all the possible APIs to surface
apis := n.apis()
for _, service := range services {
apis = append(apis, service.APIs()...)
}
我们看一下API结构的定义,代码位于rpc/types.go:
type API struct {
Namespace string // namespace under which the rpc methods of Service are exposed
Version string // api version for DApp's
Service interface{} // receiver instance which holds the methods
Public bool // indication if the methods must be considered safe for public use
}
这里有一个Namespace的概念,可以理解为API的类别分组,方便管理。用过geth客户端的同学可能有印象,我们会在命令行指定类似下面的参数:
geth --rpc --rpcapi “eth,net,web3”
这里的eth,net,web3就是Namespace,指定了需要对外暴露哪些类型的RPC API。
除了Namespace,还有一个Service字段,注意它的类型是interface{},而不是我们之前提到的Service接口,所以个人感觉这个命名不太好,改成receiver可能就不容易引起混淆了。
那么这个Service指向哪里呢?我们以eth模块为例,看一下返回的API数组里到底存的是什么内容,代码位于eth/backend.go:
func (s *Ethereum) APIs() []rpc.API {
……
// Append all the local APIs and return
return append(apis, []rpc.API{
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicMinerAPI(s),
Public: true,
},
……)
}
其他先不看,我们先看下NewPublicEthereumAPI是个什么东西,代码位于eth.api.go:
func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI {
return &PublicEthereumAPI{e}
}
type PublicEthereumAPI struct {
e *Ethereum
}
可以看到就,就是一个普通的struct,内部有一个指针指向模块本身,其实就是一个wrapper。现在再回过头来看上面那张图的黄色部分,是不是就很清楚了?
获得所有RPC API的集合后,就开始启动RPC server了。上一篇提到RPC有4种方式:InProc、IPC、HTTP、WS,在Node中对应的字段都用不同颜色标识了。流程都是类似的,这里以HTTP为例进行分析。
HTTP相关的有3个字段:
- httpEndpoint:这是一个字符串,表示IP和端口号,默认是localhost:8545
- httpListener