在Go中net/rpc标准包提供了编写RPC服务需要的系列函数。net/rpc包允许RPC客户端通过网络或者IO连接调用一个远端对象的public方法。在RPC服务端,可将一个对象注册为可访问的服务,之后该对象的公开方法就能够被远程调用。一个RPC服务端可以注册多个不同类型的对象,但是不允许注册同一类型的多个对象。
一个对象中的方法只有满足如下条件,才能被RPC服务端设置为可远程调用:
- 必须是在对象外部可访问的(首字母大写)
- 必须有两个参数,且参数类型都必须是包外部可以访问的类型或者Go内置类型
- 第二个参数必须是一个指针
- 方法必须返回一个error类型的值
用代码表示如下:
func (t* T) MethodName(argType T1, argType *T2) (error)
在这行代码中,类型T, T1, T2默认会使用Go内置的encoding/gob包进行编码解码。该方法的第一个参数表示有RPC客户端传入的参数,第二个参数表示要返回给RPC客户端的结果,最后返回一个error类型的结果。
RPC服务端可以通过调用rpc.ServeConn处理单个连接请求,但是大多数情况下,都是通过TCP或HTTP在某个网络地址上监听来创建服务。在客户端,net/rpc包提供了rpc.Dial()和rpc.DialHTTP()方法来与指定的RPC服务器建立连接。在建立连接之后,Go的net.rpc包允许我们使用同步(rpc.Call)或者异步(rpc.Go)方式接收RPC服务端的处理结果。
Server端代码:
1 package server 2 3 import ( 4 "errors" 5 "log" 6 "net" 7 "net/http" 8 "net/rpc" 9 ) 10 11 type Args struct { 12 A, B int 13 } 14 15 type Quotiten struct { 16 Quo, Rem int 17 } 18 19 type Arith int 20 21 func (t *Arith) Multiply(args *Args, reply *int) error { 22 *reply = args.A * args.B 23 return nil 24 } 25 26 func (t *Arith) Divide(args *Args, quo *Quotiten) error { 27 if args.B == 0 { 28 return errors.New("divide by zero") 29 } 30 quo.Quo = args.A / args.B 31 quo.Rem = args.A % args.B 32 return nil 33 } 34 35 func init() { 36 arith := new(Arith) 37 rpc.Register(arith) 38 rpc.HandleHTTP() 39 l, e := net.Listen("tcp", ":1234") 40 if e != nil { 41 log.Fatal("listen error: ", e) 42 } 43 go http.Serve(l, nil) 44 }
客户端调用代码
1 package main 2 3 import ( 4 "./server" 5 "fmt" 6 "log" 7 "net/rpc" 8 ) 9 10 func main() { 11 client, err := rpc.DialHTTP("tcp", "localhost:1234") 12 if nil != err { 13 log.Fatal("dialing:", err) 14 } 15 16 args := &server.Args{7, 8} 17 var reply int 18 err = client.Call("Arith.Multiply", args, &reply) 19 if nil != err { 20 log.Fatal("arith error:", err) 21 } 22 fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply) 23 }