RPC设计的目的:
隐藏客户端服务器通信细节
客户端调用更加像传统的过程调用
服务器处理更加像传统的过程调用
RPC理想是把网络通信做的跟函数调用一样。
-
Client
z = fn(x, y)
-
Server
fn(x, y) { compute return z }
*** RPC设计的目标是这种程度的透明度。 ***
服务器端
import "net/rpc"
创建远程调用对象
服务端(Register)注册一个对象,使它作为一个服务被暴露,服务的名字是该对象的类型名。注册之后,对象的导出方法就可以被远程访问。服务端可以注册多个不同类型的对象(服务),但注册具有相同类型的多个对象是错误的。
只有满足如下标准的方法才能用于远程访问,其余方法会被忽略:
- 方法是导出的
- 方法有两个参数,都是导出类型或内建类型
- 方法的第二个参数是指针
- 方法只有一个error接口类型的返回值
事实上,方法必须看起来像这样:
func (t *T) MethodName(argType T1, replyType *T2) error
比如:
type Args struct {
A, B int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
注册对象
arith := new(Arith)
rpc.Register(arith)
创建监听,接收并处理请求
rpc.HandleHTTP()
// 监听端口 1234
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error: ", e)
}
http.Serve(l, nil)
客户端
和服务器端建立连接
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing: ", err)
}
执行远程调用
执行远程调用,有两种方式:同步和异步。
// 同步调用,Call会等待远端调用完成
args := &Args{7, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
// 异步调用,Go方法异步地发送调用请求,并使用返回的Call结构体类型的Done通道字段传递完成信号
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
<-divCall.Done
fmt.Printf("Arith: %d/%d=%d...%d\n", args.A, args.B, quotient.Quo, quotient.Rem)
--End
Mason