go-ethereum之rpc

简介

主要用于DeApp与node之间的交互

结构

1 n 1 n use <<interface>> ServerCodec Server serviceRegistry service callback Client jsonCodec

Client:服务端接收到一个连接后创建Client来处理请求,表示一个到服务端的连接

go 复制代码
type Client struct {
	idgen    func() ID // for subscriptions
	isHTTP   bool      // connection type: http, ws or ipc
	services *serviceRegistry

	idCounter atomic.Uint32

	// This function, if non-nil, is called when the connection is lost.
	reconnectFunc reconnectFunc

	// config fields
	batchItemLimit       int
	batchResponseMaxSize int

	// writeConn is used for writing to the connection on the caller's goroutine. It should
	// only be accessed outside of dispatch, with the write lock held. The write lock is
	// taken by sending on reqInit and released by sending on reqSent.
	writeConn jsonWriter

	// for dispatch
	close       chan struct{}
	closing     chan struct{}    // closed when client is quitting
	didClose    chan struct{}    // closed when client quits
	reconnected chan ServerCodec // where write/reconnect sends the new connection
	readOp      chan readOp      // read messages
	readErr     chan error       // errors from read
	reqInit     chan *requestOp  // register response IDs, takes write lock
	reqSent     chan error       // signals write completion, releases write lock
	reqTimeout  chan *requestOp  // removes response IDs when call timeout expires
}

客户端创建Client使用newClient,服务端接收连接创建表示连接的客户端使用initClient。即客户端主要使用下面几个方法

  • DialOptions
  • DialHTTPWithClient(http)
  • DialInProc(inproc进程内)
  • DialIPC(进程间)
  • DialWebsocketWithDialer或者DialWebsocket(websocket)
go 复制代码
func newClient(initctx context.Context, cfg *clientConfig, connect reconnectFunc) (*Client, error) {
	conn, err := connect(initctx)
	if err != nil {
		return nil, err
	}
	c := initClient(conn, new(serviceRegistry), cfg)
	c.reconnectFunc = connect
	return c, nil
}

func initClient(conn ServerCodec, services *serviceRegistry, cfg *clientConfig) *Client {
	_, isHTTP := conn.(*httpConn)
	c := &Client{
		isHTTP:               isHTTP,
		services:             services,
		idgen:                cfg.idgen,
		batchItemLimit:       cfg.batchItemLimit,
		batchResponseMaxSize: cfg.batchResponseLimit,
		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),
	}

	// Set defaults.
	if c.idgen == nil {
		c.idgen = randomIDGenerator()
	}

	// Launch the main loop.
	if !isHTTP {
		go c.dispatch(conn)
	}
	return c
}

Server:rpc服务器

go 复制代码
type Server struct {
	services serviceRegistry
	idgen    func() ID

	mutex              sync.Mutex
	codecs             map[ServerCodec]struct{}
	run                atomic.Bool
	batchItemLimit     int
	batchResponseLimit int
	httpBodyLimit      int
	wsReadLimit        int64
}

serviceRegistry:用于管理服务器对外提供的服务

go 复制代码
type serviceRegistry struct {
	mu       sync.Mutex
	services map[string]service
}

service:对外提供的一个服务

go 复制代码
type service struct {
	name          string               // name for service
	callbacks     map[string]*callback // registered handlers
	subscriptions map[string]*callback // available subscriptions/notifications
}

callback:服务中的一个方法

go 复制代码
type callback struct {
	fn          reflect.Value  // the function
	rcvr        reflect.Value  // receiver object of method, set if fn is method
	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           // true if this is a subscription callback
}

ServerCodec :网络编解码接口

go 复制代码
type ServerCodec interface {
	peerInfo() PeerInfo
	readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
	close()

	jsonWriter
}

rpc

有四种

  • inproc(进程内)
  • ipc(同机进程间)
  • http
  • websocket
  • 认证的http和websocket

http类rpc

通过httpServer来管理
httpServer rpcHandler rpc.Server http.Handler

httpServer结构定义为

go 复制代码
type httpServer struct {
	log      log.Logger
	timeouts rpc.HTTPTimeouts
	mux      http.ServeMux // registered handlers go here

	mu       sync.Mutex
	server   *http.Server
	listener net.Listener // non-nil when server is running

	// HTTP RPC handler things.

	httpConfig  httpConfig
	httpHandler atomic.Pointer[rpcHandler]

	// WebSocket handler things.
	wsConfig  wsConfig
	wsHandler atomic.Pointer[rpcHandler]

	// These are set by setListenAddr.
	endpoint string
	host     string
	port     int

	handlerNames map[string]string
}

开启rpc

步聚为

  • 通过httpServer的enableRPC方法,设置httpHandler
  • 调用httpServer的start方法
    • 创建http.Server,设置Handler为自己(即httpServer),设置h.server
    • 创建listener
    • goroutine执行server.Serve,接收请求,执行httpServer.ServeHTTP
go 复制代码
func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
	h.mu.Lock()
	defer h.mu.Unlock()

	if h.rpcAllowed() {
		return errors.New("JSON-RPC over HTTP is already enabled")
	}

	// Create RPC server and handler.
	srv := rpc.NewServer()
	srv.SetBatchLimits(config.batchItemLimit, config.batchResponseSizeLimit)
	if config.httpBodyLimit > 0 {
		srv.SetHTTPBodyLimit(config.httpBodyLimit)
	}
	if err := RegisterApis(apis, config.Modules, srv); err != nil {
		return err
	}
	h.httpConfig = config
	h.httpHandler.Store(&rpcHandler{
		Handler: NewHTTPHandlerStack(srv, config.CorsAllowedOrigins, config.Vhosts, config.jwtSecret),
		prefix:  config.prefix,
		server:  srv,
	})
	return nil
}

func (h *httpServer) start() error {
	h.mu.Lock()
	defer h.mu.Unlock()

	if h.endpoint == "" || h.listener != nil {
		return nil // already running or not configured
	}

	// Initialize the server.
	h.server = &http.Server{Handler: h}
	if h.timeouts != (rpc.HTTPTimeouts{}) {
		CheckTimeouts(&h.timeouts)
		h.server.ReadTimeout = h.timeouts.ReadTimeout
		h.server.ReadHeaderTimeout = h.timeouts.ReadHeaderTimeout
		h.server.WriteTimeout = h.timeouts.WriteTimeout
		h.server.IdleTimeout = h.timeouts.IdleTimeout
	}

	// Start the server.
	listener, err := net.Listen("tcp", h.endpoint)
	if err != nil {
		// If the server fails to start, we need to clear out the RPC and WS
		// configuration so they can be configured another time.
		h.disableRPC()
		h.disableWS()
		return err
	}
	h.listener = listener
	go h.server.Serve(listener)

	if h.wsAllowed() {
		url := fmt.Sprintf("ws://%v", listener.Addr())
		if h.wsConfig.prefix != "" {
			url += h.wsConfig.prefix
		}
		h.log.Info("WebSocket enabled", "url", url)
	}
	// if server is websocket only, return after logging
	if !h.rpcAllowed() {
		return nil
	}
	// Log http endpoint.
	h.log.Info("HTTP server started",
		"endpoint", listener.Addr(), "auth", h.httpConfig.jwtSecret != nil,
		"prefix", h.httpConfig.prefix,
		"cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","),
		"vhosts", strings.Join(h.httpConfig.Vhosts, ","),
	)

	// Log all handlers mounted on server.
	var paths []string
	for path := range h.handlerNames {
		paths = append(paths, path)
	}
	sort.Strings(paths)
	logged := make(map[string]bool, len(paths))
	for _, path := range paths {
		name := h.handlerNames[path]
		if !logged[name] {
			log.Info(name+" enabled", "url", "http://"+listener.Addr().String()+path)
			logged[name] = true
		}
	}
	return nil
}

func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// check if ws request and serve if ws enabled
	ws := h.wsHandler.Load()
	if ws != nil && isWebsocket(r) {
		if checkPath(r, ws.prefix) {
			ws.ServeHTTP(w, r)
		}
		return
	}

	// if http-rpc is enabled, try to serve request
	rpc := h.httpHandler.Load()
	if rpc != nil {
		// First try to route in the mux.
		// Requests to a path below root are handled by the mux,
		// which has all the handlers registered via Node.RegisterHandler.
		// These are made available when RPC is enabled.
		muxHandler, pattern := h.mux.Handler(r)
		if pattern != "" {
			muxHandler.ServeHTTP(w, r)
			return
		}

		if checkPath(r, rpc.prefix) {
			rpc.ServeHTTP(w, r)
			return
		}
	}
	w.WriteHeader(http.StatusNotFound)
}
相关推荐
yong99902 小时前
MATLAB倍频转换效率分析与最佳匹配角模拟
开发语言·前端·matlab
已黑化的小白2 小时前
Rust 的所有权系统,是一场对“共享即混乱”的编程革命
开发语言·后端·rust
csbysj20202 小时前
TypeScript 元组
开发语言
Laity______3 小时前
指针(2)
c语言·开发语言·数据结构·算法
是苏浙3 小时前
零基础入门C语言之C语言实现数据结构之顺序表经典算法
c语言·开发语言·数据结构·算法
5967851543 小时前
C# 弹出框DialogForm
开发语言·c#
FnTop3 小时前
实用教程:打造支持参数配置的 Git Bash 文件清理脚本
开发语言·git·bash
提娜米苏3 小时前
Bash Shell脚本学习——唇读数据集验证脚本
开发语言·学习·bash
lkbhua莱克瓦243 小时前
Java基础——集合进阶5
java·开发语言·集合·泛型