Overview
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 main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
}
golang 实现的 RPC 是基于 HTTP 的,RPC Server 实际上就是一个 Http Server,通过将 Service 注册到 Server 上,就可以通过 Clinet 调用 service 上的函数。
图一:overview
Server
rpc 对 http 的侵入是根据 http 的 Hijacker 实现的,ResponseWriter
实现了 Hijack
方法,能够从中获取到 conn,通过 conn 生成 Encoder 和 Decoder。
为什么 Encoder 需要从 buf 上生成,而 Decoder 直接从 conn 上就可以?
图二: ServeHTTP
Server 通过两层 map 能够找对对应 Service 对应的方法,这里大量使用了反射的方法。
图三:Register
Client
图四:Call
对于每一个 Request,会生成一个 seq 作为 key,将其存储到 map 中。接收到 Response 后会根据里面存储的 seq 确定哪一个是对应的 Call。
这里有两种方式发起 RPC 调用,Go
是异步的,Call
是同步的,通过监听 Call 的 done channel,一直阻塞到 Response 返回。
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
// ...
return call
}
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}