搭建过以太坊私链的同学或许都通过console或者Postman第三方工具去查询以太坊主链的相关信息,如通过区块高度查询区块信息。在以太坊的底层,这些都是通过RPC的调用去实现的,今天就来看看以太坊底层RPC启动方面的源码,了解一下RPC的启动过程。
RPC在以太坊源码中的启动步骤如下:
----- geth
-------- startNode
----------- utils.StartNode
-------------- Node.Start()
----------------- Node.startRPC
上述是RPC在以太坊启动过程中的调用路径,下面先来介绍一下与RPC相关的几个重要的数据结构:
type API struct {
Namespace string
Version string
Service interface{}
Public bool
}
Namespace:命令空间,对模块进行命名,eg:eth,net,web3
Version :模块的版本信息
Service :执行模块的服务函数
Public :模式是否运行外部调用
type Server struct {
services serviceRegistry
run int32
codecsMu sync.Mutex
codecs *set.Set
}
run:该服务是否运行的状态
codecsMu :sync的互斥锁
codecs :
services :是一个map,key是Namespace,value是一个service实例。定义:type serviceRegistry map[string]*service
下面看看server的定义:
type service struct {
name string
typ reflect.Type
callbacks callbacks
subscriptions subscriptions
}
name :模块名称
typ:接收的类型
callbacks : 是一个map,key是Namespace,value是一个callback 实例。定义: type callbacks map[string]*callback
subscriptions : 是一个map,key是Namespace,value是一个callback 实例。定义: type subscriptions map[string]*callback
下面看看callback的定义:
type callback struct {
rcvr reflect.Value // receiver of method
method reflect.Method // callback
argTypes []reflect.Type // input argument types
hasCtx bool // method's first argument is a context (not included in argTypes)
errPos int // err return idx, of -1 when method cannot return error
isSubscribe bool // indication if the callback is a subscription
}
rcvr:方法的接收者,这是一个反射值类型,其实就是指向了之前的NewPublicEthereumAPI
method:对应rcvr中的函数
argTypes:函数参数的类型列表
hasCtx:标识函数的第一个参数是否是context.Context类型
errPos:错误代码数值
isSubscribe:是否是subscription类型
接下来我们一起看看RPC启动的源码:
源码路径:go-ethereum\node\node.go
func (n *Node) startRPC(services map[reflect.Type]Service) error {
//创建apis
apis := n.apis()
for _, service := range services {
apis = append(apis, service.APIs()...)
}
//基于apis启动服务
if err := n.startInProc(apis); err != nil {
return err
}
if err := n.startIPC(apis); err != nil {
n.stopInProc()
return err
}
if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil {
n.stopIPC()
n.stopInProc()
return err
}
if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
n.stopHTTP()
n.stopIPC()
n.stopInProc()
return err
}
// All API endpoints started successfully
n.rpcAPIs = apis
return nil
}
startInProc:内部调用,没有启动相关Server服务
startIPC:启动IPC服务
startHTTP:启动http服务
startWS:启动WebSocket服务
如上述服务有一项启动失败,整体RPC服务启动失败,报错并退出!下面重点介绍一下httpRPC的启动过程,源码如下:
func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error {
// http的IP和端口,默认localhost:8545
if endpoint == "" {
return nil
}
listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts)
if err != nil {
return err
}
n.log.Info("HTTP en