客户端:服务调用发起方,又称之为服务消费者 服务器:远端计算机上运行的程序,其中包含客户端要调用和访问的方法 客户端存根:存放服务器端的地址,端口消息,将客户端的请求参数打包成网络消息,发送给服务器方。接收服务器方返回的数据包。该段程序运行在客户端。 服务端存根:接收客户端发送的数据包,解析数据包,调用数据包,调用具体的服务方法,将调用结果打包发送给客户端一方。该段程序运行在服务端。编程
工做过程:json
一、客户端想要发起一个远程过程调用,首先经过调用本地客户端Stub程序的方式调用想要使用的功能方法名;服务器
二、客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息作序列化操做,并打包成数据包。网络
三、客户端Stub查找到远程服务器程序的IP地址,调用Socket通讯协议,经过网络发送给服务端。异步
四、服务端Stub程序接收到客户端发送的数据包信息,并经过约定好的协议将数据进行反序列化,获得请求的方法名和请求参数等信息。tcp
五、服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。ide
六、服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。编码
七、服务端Stub程序将程序调用结果按照约定的协议进行序列化,并经过网络发送回客户端Stub程序。代理
八、客户端Stub程序接收到服务端Stub发送的返回数据,对数据进行反序列化操做,并将调用返回的数据传递给客户端请求发起者。指针
九、客户端请求发起者获得调用结果,整个RPC调用过程结束。
RPC涉及到的相关技术 经过上文一系列的文字描述和讲解,咱们已经了解了RPC的由来和RPC整个调用过程。咱们能够看到RPC是一系列操做的集合,其中涉及到不少对数据的操做,以及网络通讯。所以,咱们对RPC中涉及到的技术作一个总结和分析:
一、动态代理技术: 上文中咱们提到的Client Stub和Sever Stub程序,在具体的编码和开发实践过程当中,都是使用动态代理技术自动生成的一段程序。
二、序列化和反序列化: 在RPC调用的过程当中,咱们能够看到数据须要在一台机器上传输到另一台机器上。在互联网上,全部的数据都是以字节的形式进行传输的。而咱们在编程的过程当中,每每都是使用数据对象,所以想要在网络上将数据对象和相关变量进行传输,就须要对数据对象作序列化和反序列化的操做。
序列化:把对象转换为字节序列的过程称为对象的序列化,也就是编码的过程。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化,也就是解码的过程。
服务定义及暴漏:
func (t *T) MethodName(request T1,response *T2) error 上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是: 一、对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。 二、方法的第二个参数必须是指针类型。 三、方法的返回类型为error。 四、方法的类型是可输出的。 * 五、方法自己也是可输出的。
type MathUtil struct{}//该方法向外暴露:提供计算圆形面积的服务 func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error { *resp = math.Pi * req * req //圆形的面积 s = π * r * r return nil //返回类型 }
代码讲解: 在上述的案例中,咱们能够看到: 一、Calculate方法是服务对象MathUtil向外提供的服务方法,该方法用于接收传入的圆形半径数据,计算圆形面积并返回。 二、第一个参数req表明的是调用者(client)传递提供的参数。 三、第二个参数resp表明要返回给调用者的计算结果,必须是指针类型。 四、正常状况下,方法的返回值为是error,为nil。若是遇到异常或特殊状况,则error将做为一个字符串返回给调用者,此时,resp参数就不会再返回给调用者。
客户端链接服务端:
client, err := rpc.DialHTTP("tcp", "localhost:8081") if err != nil { panic(err.Error()) }
远端方法调用 客户端成功链接服务端之后,就能够经过方法调用调用服务端的方法,具体调用方法以下:
var req float32 //请求值 req = 3 var resp float32 //返回值 err = client.Call("MathUtil.CalculateCircleArea", req, &resp) if err != nil { panic(err.Error()) } fmt.Println(resp)
上述的调用方法核心在于client.Call方法的调用,该方法有三个参数,第一个参数表示要调用的远端服务的方法名,第二个参数是调用时要传入的参数,第三个参数是调用要接收的返回值。 上述的Call方法调用实现的方式是同步的调用,除此以外,还有一种异步的方式能够实现调用。异步调用代码实现以下:
var respSync *float32 //异步的调用方式 syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil) replayDone := <-syncCall.Done fmt.Println(replayDone) fmt.Println(*respSync)
代码演示:
Go HTTPRPC
//server package main import ( "errors" "fmt" "log" "net/http" "net/rpc" ) //使用 rpc http实现简单的 rpc操做 type Args struct { A, B int } type Math int func (m *Math) Multiply(args *Args, reply *int) error { *reply = args.A * args.B return nil } type Quotient struct { Quo, Rem int } func (m *Math) 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() { math := new(Math) rpc.Register(math) //注册rpc rpc.HandleHTTP() //使用http rpc fmt.Println("rpc http server runing ....") err := http.ListenAndServe(":1234", nil) if err != nil { log.Println(err.Error()) } } //client package main import ( "fmt" "log" "net/rpc" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } func main() { // fmt.Println(os.Args) // if len(os.Args) != 2 { // fmt.Println("usage:", os.Args[0], "server") // os.Exit(1) // } serverAddr := "127.0.0.1" client, err := rpc.DialHTTP("tcp", serverAddr+":1234") if err != nil { log.Println("dial err is ", err) } var reply int args := Args{1, 2} err = client.Call("Math.Multiply", args, &reply) //server method方法名要与server一致,写错后,将会提示服务不存在 if err != nil { log.Println("call err ", err) } fmt.Printf("Math Multiply: %d * %d =%d \n", args.A, args.B, reply) var quo Quotient err = client.Call("Math.Divide", args, &quo) if err != nil { log.Println("divide err ", err) } fmt.Printf("Math Divide: %d / %d =%d remainder %d \n", args.A, args.B, quo.Quo, quo.Rem) }
Go TcpRPC
//server package main import ( "errors" "log" "net" "net/rpc" ) type Math int type Args struct { A, B int } func (m *Math) Multiply(args *Args, reply *int) error { *reply = args.A * args.B return nil } type Quotient struct { Quo, Rem int } func (m *Math) 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() { math := new(Math) rpc.Register(math) tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234") if err != nil { log.Println("Resolve Ip addr err ", err) return } listen, err := net.ListenTCP("tcp", tcpAddr) if err != nil { log.Println("listen err is ", err) return } for { server, err := listen.Accept() if err != nil { log.Println("accept err is ", err) continue } rpc.ServeConn(server) } } //client package main import ( "fmt" "log" "net/rpc" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } func main() { client, err := rpc.Dial("tcp", "127.0.0.1"+":1234") if err != nil { log.Println("rpc dial err :", err) return } args := Args{1, 2} var reply int err = client.Call("Math.Multiply", args, &reply) if err != nil { log.Println("multiply err ", err) return } fmt.Printf("multiply %d * %d = %d \n", args.A, args.B, reply) var quo Quotient err = client.Call("Math.Divide", args, &quo) if err != nil { log.Println("Divite err is ", err) return } fmt.Printf("Divide %d / %d =%d ,rem %d", args.A, args.B, quo.Quo, quo.Rem) }
Go JSONRPC
//server package main import ( "net/rpc" "net/rpc/jsonrpc" "log" "net" "errors" ) type Math int type Args struct { A, B int } type Quotient struct { Quo, Rem int } func (m *Math)Multiply(args Args,reply *int) error{ *reply=args.A*args.B return nil } func (m *Math)Divide(args Args,quo *Quotient) error{ if args.B==0{ return errors.New("divide by zreo") } quo.Quo=args.A/args.B quo.Rem=args.A%args.B return nil } func main(){ math:=new(Math) rpc.Register(math) listenAddr,err:=net.ResolveTCPAddr("tcp",":1234") if err!=nil{ log.Println("resove ip addr err is ",err) return } listen,err:=net.ListenTCP("tcp",listenAddr) if err!=nil{ log.Println("listenTcp err ",err) return } for{ conn,err:=listen.Accept() if err!=nil{ log.Println("accept err is ",err) return } jsonrpc.ServeConn(conn) } } //client package main import ( "fmt" "log" "net/rpc/jsonrpc" ) type Args struct { A, B int } type Quotient struct { Quo, Rem int } func main() { client, err := jsonrpc.Dial("tcp", "127.0.0.1"+":1234") if err != nil { log.Println("rpc dial err :", err) return } args := Args{1, 2} var reply int err = client.Call("Math.Multiply", args, &reply) if err != nil { log.Println("multiply err ", err) return } fmt.Printf("multiply %d * %d = %d \n", args.A, args.B, reply) var quo Quotient err = client.Call("Math.Divide", args, &quo) if err != nil { log.Println("Divite err is ", err) return } fmt.Printf("Divide %d / %d =%d ,rem %d", args.A, args.B, quo.Quo, quo.Rem) }