GO-HPB源码解读--PRC服务启动

RPC服务是在节点启动的时候通过参数–rpcaddr 0.0.0.0 –rpcport 8541来指定的,启动代码是:

1
2
hpbnode.SetNodeAPI()
hpbnode.Hpbrpcmanager.Start(hpbnode.RpcAPIs)

其中SetNodeAPI是把所有的API定义以[]rpc.API的数据类型保存hpbnode.RpcAPIs,然后再传给Start方法。API的结构如下,其中public为false的话是不会发布出去了。

1
2
3
4
5
6
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
}

然后启动RPC服务,可以看到一共指定了三种形式的服务参数,分别是ipc、http、ws,ipc是进程间通信,在同一机器上进行的内存共享方式通信,由于不经过网卡转输数据,所以可以节省网络资源;http就是我们通过web3接口调用的通信服务,ws是WebService是一种严格定义报文格式的协议,底层也是基于http的,这里我们主要看下http服务。
如果后启动的服务出现异常,则需要把所有已启动的服务停止掉,并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func (prm *RpcManager)Start(apis []API ) error {

config :=config.GetHpbConfigInstance()
// for-test
log.Debug("Para from config.","IpcEndpoint",config.Network.IpcEndpoint,"HttpEndpoint",config.Network.HttpEndpoint,"WsEndpoint",config.Network.WsEndpoint)

prm.rpcmgr = &RpcMgr{
ipcEndpoint: config.Network.IpcEndpoint,
httpEndpoint: config.Network.HttpEndpoint,
wsEndpoint: config.Network.WsEndpoint,

httpCors: config.Network.HTTPCors,
httpModules: config.Network.HTTPModules,

httpVirtualHosts:config.Network.HTTPVirtualHosts,
httpTimeouts: config.Network.HTTPTimeouts,

wsOrigins: config.Network.WSOrigins,
wsModules: config.Network.WSModules,
wsExposeAll: config.Network.WSExposeAll,
}
if err := prm.rpcmgr.startRPC(apis); err != nil {
log.Error("start rpc error","reason",err)
}
return nil
}

startRPC方法中按照先后顺序分别启动了四种形式的服务,多了一种InProc,这个是进程内通信(具体使用还没研究)。这里我们就看一下startHTTP的启动过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// startRPC is a helper method to start all the various RPC endpoint during node
// startup. It's not meant to be called at any time afterwards as it makes certain
// assumptions about the state of the node.
func (n *RpcMgr) startRPC(apis []API) error {
// Gather all the possible APIs to surface
n.rpcAPIs = apis

// Start the various API endpoints, terminating all in case of errors
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.httpModules, n.httpCors, n.httpVirtualHosts, n.httpTimeouts); err != nil {
n.stopIPC()
n.stopInProc()
return err
}
if err := n.startWS(n.wsEndpoint, apis, n.wsModules, n.wsOrigins, n.wsExposeAll); err != nil {
n.stopHTTP()
n.stopIPC()
n.stopInProc()
return err
}
// All API endpoints started successfully
return nil
}

在StartHTTPEndpoint方法中,modules就是在启动的时候通过参数–rpcapi hpb,web3,admin,txpool,debug,personal,net指定的,表示启动的模块,RPC在启动的时候会检查程序所定的API是否在modules中,只有在的才会启动,另外还有个条件就是api是Public的。NewHTTPServer方法启动服务,其中cors参数是跨域域名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules
func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts config.HTTPTimeouts) (net.Listener, *Server, error) {
// Generate the whitelist based on the allowed modules
whitelist := make(map[string]bool)
for _, module := range modules {
whitelist[module] = true
}
// Register all the APIs exposed by the services
handler := NewServer()
for _, api := range apis {
if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return nil, nil, err
}
log.Debug("HTTP registered", "namespace", api.Namespace)
}
}
// All APIs registered, start the HTTP listener
var (
listener net.Listener
err error
)
if listener, err = net.Listen("tcp", endpoint); err != nil {
return nil, nil, err
}
go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener)
return listener, handler, err
}