RPC supports 4 different transports:
- InProc
- IPC
- WS
- HTTP
Init
When node startup, APIs are registered as call back.
In Node.startInProc(), all APIs will be registered as RPC callbacks.
// startInProc initializes an in-process RPC endpoint.
func (n *Node) startInProc(apis []rpc.API) error {
// Register all the APIs exposed by the services
handler := rpc.NewServer()
for _, api := range apis {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return err
}
n.log.Debug("InProc registered", "namespace", api.Namespace)
}
n.inprocHandler = handler
return nil
}
call stack:
github.com/ethereum/go-ethereum/rpc.(*serviceRegistry).registerName at service.go:89
github.com/ethereum/go-ethereum/rpc.(*Server).RegisterName at server.go:66
github.com/ethereum/go-ethereum/node.(*Node).startInProc at node.go:315
github.com/ethereum/go-ethereum/node.(*Node).startRPC at node.go:287
github.com/ethereum/go-ethereum/node.(*Node).Start at node.go:239
github.com/ethereum/go-ethereum/cmd/utils.StartNode at cmd.go:67
main.startNode at main.go:331
main.geth at main.go:308
gopkg.in/urfave/cli%2ev1.HandleAction at app.go:490
gopkg.in/urfave/cli%2ev1.(*App).Run at app.go:264
main.main at main.go:248
runtime.main at proc.go:203
runtime.goexit at asm_amd64.s:1357
- Async stack trace
runtime.rt0_go at asm_amd64.s:220
IPC, WS, HTTP have the similar mechanism of registering callbacks.
See:
StartHTTPEndpoint
StartWSEndpoint
StartIPCEndpoint
Start
In general, RPC server listens and wait for inbound connection and then start a goroutine to dispatch all RPC requests.
// StartIPCEndpoint starts an IPC endpoint.
func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) {
// Register all the APIs exposed by the services.
handler := NewServer()
for _, api := range apis {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return nil, nil, err
}
log.Debug("IPC registered", "namespace", api.Namespace)
}
// All APIs registered, start the IPC listener.
listener, err := ipcListen(ipcEndpoint)
if err != nil {
return nil, nil, err
}
go handler.ServeListener(listener)
return listener, handler, nil
}
// ServeListener accepts connections on l, serving JSON-RPC on them.
func (s *Server) ServeListener(l net.Listener) error {
for {
conn, err := l.Accept()
if netutil.IsTemporaryError(err) {
log.Warn("RPC accept error", "err", err)
continue
} else if err != nil {
return err
}
log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr())
go s.ServeCodec(NewCodec(conn), 0)
}
}
func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client {
_, isHTTP := conn.(*httpConn)
c := &Client{
idgen: idgen,
isHTTP: isHTTP,
services: services,
writeConn: conn,
close: make(chan struct{}),
closing: make(chan struct{}),
didClose: make(chan struct{}),
reconnected: make(chan ServerCodec),
readOp: make(chan readOp),
readErr: make(chan error),
reqInit: make(chan *requestOp),
reqSent: make(chan error, 1),
reqTimeout: make(chan *requestOp),
}
if !isHTTP {
go c.dispatch(conn)
}
return c
}
The dispatch() will spawn a gorouting to read op from codec, and run a loop for hanlding op.
ServerCodec
// ServerCodec implements reading, parsing and writing RPC messages for the server side of
// a RPC session. Implementations must be go-routine safe since the codec can be called in
// multiple go-routines concurrently.
type ServerCodec interface {
readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
close()
jsonWriter
}
- httpCodec, RPC client that connects to an RPC server over HTTP
- jsonCodec, JSON-RPC messages to the underlying connection
InProc
DialInProc makes a pipe, for communicatio between server and client.
github.com/ethereum/go-ethereum/rpc.initClient at client.go:206
github.com/ethereum/go-ethereum/rpc.newClient at client.go:200
github.com/ethereum/go-ethereum/rpc.DialInProc at inproc.go:27
github.com/ethereum/go-ethereum/node.(*Node).Attach at node.go:515
main.startNode at main.go:341
main.geth at main.go:308
gopkg.in/urfave/cli%2ev1.HandleAction at app.go:490
gopkg.in/urfave/cli%2ev1.(*App).Run at app.go:264
main.main at main.go:248
runtime.main at proc.go:203
runtime.goexit at asm_amd64.s:1357
- Async stack trace
runtime.rt0_go at asm_amd64.s:220
IPC
github.com/ethereum/go-ethereum/rpc.initClient at client.go:206
github.com/ethereum/go-ethereum/rpc.(*Server).ServeCodec at server.go:86
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/ethereum/go-ethereum/rpc.(*Server).ServeListener at ipc.go:38
WS
…
HTTP
…
Invoke Method
Sample call stack of ‘admin.datadir’
dispatch:
github.com/ethereum/go-ethereum/rpc.(*handler).startCallProc at handler.go:221
github.com/ethereum/go-ethereum/rpc.(*handler).handleMsg at handler.go:138
github.com/ethereum/go-ethereum/rpc.(*Client).dispatch at client.go:557
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/ethereum/go-ethereum/rpc.initClient at client.go:223
goroutine for invokation:
github.com/ethereum/go-ethereum/node.(*PublicAdminAPI).Datadir at api.go:295
runtime.call32 at asm_amd64.s:539
reflect.Value.call at value.go:460
reflect.Value.Call at value.go:321
github.com/ethereum/go-ethereum/rpc.(*callback).call at service.go:206
github.com/ethereum/go-ethereum/rpc.(*handler).runMethod at handler.go:369
github.com/ethereum/go-ethereum/rpc.(*handler).handleCall at handler.go:331
github.com/ethereum/go-ethereum/rpc.(*handler).handleCallMsg at handler.go:298
github.com/ethereum/go-ethereum/rpc.(*handler).handleMsg.func1 at handler.go:139
github.com/ethereum/go-ethereum/rpc.(*handler).startCallProc.func1 at handler.go:226
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/ethereum/go-ethereum/rpc.(*handler).startCallProc at handler.go:222
API
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0