Overview

  1. type Args struct {
  2. A, B int
  3. }
  4. type Quotient struct {
  5. Quo, Rem int
  6. }
  7. type Arith int
  8. func (t *Arith) Multiply(args *Args, reply *int) error {
  9. *reply = args.A * args.B
  10. return nil
  11. }
  12. func (t *Arith) Divide(args *Args, quo *Quotient) error {
  13. if args.B == 0 {
  14. return errors.New("divide by zero")
  15. }
  16. quo.Quo = args.A / args.B
  17. quo.Rem = args.A % args.B
  18. return nil
  19. }
  20. func main() {
  21. arith := new(Arith)
  22. rpc.Register(arith)
  23. rpc.HandleHTTP()
  24. l, e := net.Listen("tcp", ":1234")
  25. if e != nil {
  26. log.Fatal("listen error:", e)
  27. }
  28. go http.Serve(l, nil)
  29. }

golang 实现的 RPC 是基于 HTTP 的,RPC Server 实际上就是一个 Http Server,通过将 Service 注册到 Server 上,就可以通过 Clinet 调用 service 上的函数。
go-rpc-overview.png
图一:overview

Server

rpc 对 http 的侵入是根据 http 的 Hijacker 实现的,ResponseWriter 实现了 Hijack 方法,能够从中获取到 conn,通过 conn 生成 Encoder 和 Decoder。

为什么 Encoder 需要从 buf 上生成,而 Decoder 直接从 conn 上就可以?

go-rpc-ServeHTTP.png
图二: ServeHTTP

Server 通过两层 map 能够找对对应 Service 对应的方法,这里大量使用了反射的方法。
go-rpc-Register.png
图三:Register

Client

go-rpc-Call.png
图四:Call
对于每一个 Request,会生成一个 seq 作为 key,将其存储到 map 中。接收到 Response 后会根据里面存储的 seq 确定哪一个是对应的 Call。

这里有两种方式发起 RPC 调用,Go 是异步的,Call 是同步的,通过监听 Call 的 done channel,一直阻塞到 Response 返回。

  1. func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
  2. // ...
  3. return call
  4. }
  5. func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
  6. call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
  7. return call.Error
  8. }