Go标准库中RPC初始化过程

197 阅读2分钟

先上一段官方给的示例代码,


type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func StartServer() {

	// server
	go func() {
		arith := new(Arith)
		rpc.Register(arith)
		rpc.HandleHTTP()
		l, e := net.Listen("tcp", ":1234")
		if e != nil {
			log.Fatal("listen error:", e)
		}
		http.Serve(l, nil)
	}()

	// client
	out := make(chan struct{})
	go func() {
		serverAddress := "127.0.0.1"
		client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
		if err != nil {
			log.Fatal("dialing:", err)
		}

		// Synchronous call
		args := &Args{7, 8}
		var reply int
		err = client.Call("Arith.Multiply", args, &reply)
		if err != nil {
			log.Fatal("arith error:", err)
		}
		fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
		out <- struct{}{}
	}()
	<-out
}

其中,最令人难以理解的是下面这段代码:

    rpc.Register(arith)
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1234")
    if e != nil {
    	log.Fatal("listen error:", e)
    }
    http.Serve(l, nil)

在分别执行了rpc.Registerrpc.HandleHTTP方法之后,为什么就可以直接使用http.Serve监听客户端的请求呢?

翻了一下代码,代码的逻辑方式大致如下:

rpc.HandleHTTP 实现

1、调用rpc.HandleHTTP,会直接使用DefaultServer操作逻辑, DefaultServer是一个初始化的rpc.Server指针。

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{}
}

// DefaultServer is the default instance of *Server.
var DefaultServer = NewServer()

...
func HandleHTTP() {
	DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}

2、调用(server *Server) HandleHTTP函数,使用DefaultServeMux.Handle(pattern, handler)来处理,patternhandler,这里的handdler就是rpc.DefaultServer

func (server *Server) HandleHTTP(rpcPath, debugPath string) {
	http.Handle(rpcPath, server)
	http.Handle(debugPath, debugHTTP{server})
}
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

3、紧接着,将patternhandler封装到了ServeMux结构中,即DefaultServeMux中。

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    ...
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}
	...
}

http.Serve 实现

1、调用http.Server函数,初始化服务

func Serve(l net.Listener, handler Handler) error {
	srv := &Server{Handler: handler}
	return srv.Serve(l)
}

2、创建一个连接conn,将当前srv包装起来,然后调用serve函数

func (srv *Server) Serve(l net.Listener) error {
    ...
	for {
	    ... 
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)
	}
}
func (srv *Server) newConn(rwc net.Conn) *conn {
	c := &conn{
		server: srv,
		rwc:    rwc,
	}
    ...
}

3、调用(sh serverHandler) ServeHTTP方法,确定handler调用,如果handler == nil,则使用DefaultServeMux,正好合RPC初始化的配置对应上。

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
	srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	if handler == nil { // 本文的handler=nil,直接使用DefaultServeMux
		handler = DefaultServeMux 
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)
}

func (c *conn) serve(ctx context.Context) {
    
	// HTTP cannot have multiple simultaneous active requests.[*]
	// Until the server replies to this request, it can't read another,
	// so we might as well run the handler in this goroutine.
	// [*] Not strictly true: HTTP pipelining. We could let them all process
	// in parallel even if their responses need to be serialized.
	// But we're not going to implement HTTP pipelining because it
	// was never deployed in the wild and the answer is HTTP/2.
	serverHandler{c.server}.ServeHTTP(w, w.req)
}