RPC包概述

RPC包主要的服务逻辑在server.go和subscription.go包中。接口的定义在types.go中。
RPC包主要实现在启动节点的时候,将自己写的api包通过反射的形式将方法名和调用的api绑定。在启动命令行之后,通过输入命令的形式,通过RPC方法找到对应的方法调用,获取返回值。

RPC方法追踪

首先,在geth启动时,geth中有startNode方法,通过层层跟踪我们进入到了Node.Start()方法中。
在start方法中,有一个startRPC方法,启动节点的RPC。

  1. // startRPC is a helper method to start all the various RPC endpoint during node
  2. // startup. It's not meant to be called at any time afterwards as it makes certain
  3. // assumptions about the state of the node.
  4. func (n *Node) startRPC(services map[reflect.Type]Service) error {
  5. // Gather all the possible APIs to surface
  6. apis := n.apis()
  7. for _, service := range services {
  8. apis = append(apis, service.APIs()...)
  9. }
  10. // Start the various API endpoints, terminating all in case of errors
  11. if err := n.startInProc(apis); err != nil {
  12. return err
  13. }
  14. if err := n.startIPC(apis); err != nil {
  15. n.stopInProc()
  16. return err
  17. }
  18. if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors); err != nil {
  19. n.stopIPC()
  20. n.stopInProc()
  21. return err
  22. }
  23. if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
  24. n.stopHTTP()
  25. n.stopIPC()
  26. n.stopInProc()
  27. return err
  28. }
  29. // All API endpoints started successfully
  30. n.rpcAPIs = apis
  31. return nil
  32. }

这里,startRPC方法在执行时就会去读取api,然后暴露各个api。
apis()的定义如下:

  1. // apis returns the collection of RPC descriptors this node offers.
  2. func (n *Node) apis() []rpc.API {
  3. return []rpc.API{
  4. {
  5. Namespace: "admin",
  6. Version: "1.0",
  7. Service: NewPrivateAdminAPI(n),
  8. }, {
  9. Namespace: "admin",
  10. Version: "1.0",
  11. Service: NewPublicAdminAPI(n),
  12. Public: true,
  13. }, {
  14. Namespace: "debug",
  15. Version: "1.0",
  16. Service: debug.Handler,
  17. }, {
  18. Namespace: "debug",
  19. Version: "1.0",
  20. Service: NewPublicDebugAPI(n),
  21. Public: true,
  22. }, {
  23. Namespace: "web3",
  24. Version: "1.0",
  25. Service: NewPublicWeb3API(n),
  26. Public: true,
  27. },
  28. }
  29. }

其中,Namespace是我们定义的包名,即在命令行中可以调用的方法。
Version是这个包的版本号。 Service是所映射的API管理的结构体,这里API的方法需要满足RPC的标准才能通过校验。
成为RPC调用方法标准如下:

  1. ·对象必须导出<br>
  2. ·方法必须导出<br>
  3. ·方法返回01(响应或错误)或2(响应和错误)值<br>
  4. ·方法参数必须导出或是内置类型<br>
  5. ·方法返回值必须导出或是内置类型<br>

在将各个API都写入到列表中之后,然后启动多个API endpoints。
这里我们以启动IPC为例,主要看startIPC方法。

  1. func (n *Node) startIPC(apis []rpc.API) error {
  2. // Short circuit if the IPC endpoint isn't being exposed
  3. if n.ipcEndpoint == "" {
  4. return nil
  5. }
  6. // Register all the APIs exposed by the services
  7. handler := rpc.NewServer()
  8. for _, api := range apis {
  9. if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
  10. return err
  11. }
  12. n.log.Debug(fmt.Sprintf("IPC registered %T under '%s'", api.Service, api.Namespace))
  13. }
  14. ...

这里会首先启创建一个rpc server。在启动的过程中,rpc server会将自己注册到handler中,即rpc包。
在创建rpc server之后,handler会通过RegisterName方法将暴露的方法注册到rpc server中。

  1. // RegisterName will create a service for the given rcvr type under the given name. When no methods on the given rcvr
  2. // match the criteria to be either a RPC method or a subscription an error is returned. Otherwise a new service is
  3. // created and added to the service collection this server instance serves.
  4. func (s *Server) RegisterName(name string, rcvr interface{}) error {
  5. if s.services == nil {
  6. s.services = make(serviceRegistry)
  7. }
  8. svc := new(service)
  9. svc.typ = reflect.TypeOf(rcvr)
  10. rcvrVal := reflect.ValueOf(rcvr)
  11. if name == "" {
  12. return fmt.Errorf("no service name for type %s", svc.typ.String())
  13. }
  14. if !isExported(reflect.Indirect(rcvrVal).Type().Name()) {
  15. return fmt.Errorf("%s is not exported", reflect.Indirect(rcvrVal).Type().Name())
  16. }
  17. methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ)
  18. // already a previous service register under given sname, merge methods/subscriptions
  19. if regsvc, present := s.services[name]; present {
  20. if len(methods) == 0 && len(subscriptions) == 0 {
  21. return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
  22. }
  23. for _, m := range methods {
  24. regsvc.callbacks[formatName(m.method.Name)] = m
  25. }
  26. for _, s := range subscriptions {
  27. regsvc.subscriptions[formatName(s.method.Name)] = s
  28. }
  29. return nil
  30. }
  31. svc.name = name
  32. svc.callbacks, svc.subscriptions = methods, subscriptions
  33. if len(svc.callbacks) == 0 && len(svc.subscriptions) == 0 {
  34. return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
  35. }
  36. s.services[svc.name] = svc
  37. return nil
  38. }

在RegisterName方法中,这个方法会将所提供包下所有符合RPC调用标准的方法注册到Server的callback调用集合中等待调用。
这里,筛选符合条件的RPC调用方法又suitableCallbacks方法实现。
这样就将对应包中的方法注册到Server中,在之后的命令行中即可调用。

参考资料

RPC包的官方文档