Golang中怎么实现一个RPC功能
今天就跟大家聊聊有关Golang中怎么实现一个RPC功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
创新互联建站来电联系:18980820575,为您提供成都网站建设网页设计及定制高端网站建设服务,创新互联建站网页制作领域十余年,包括成都玻璃隔断等多个行业拥有多年的网站设计经验,选择创新互联建站,为网站保驾护航。
客户端:服务调用发起方,又称之为服务消费者 服务器:远端计算机上运行的程序,其中包含客户端要调用和访问的方法 客户端存根:存放服务器端的地址,端口消息,将客户端的请求参数打包成网络消息,发送给服务器方。接收服务器方返回的数据包。该段程序运行在客户端。 服务端存根:接收客户端发送的数据包,解析数据包,调用数据包,调用具体的服务方法,将调用结果打包发送给客户端一方。该段程序运行在服务端。
工作过程:
1、客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
2、客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
3、客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
4、服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
5、服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
6、服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
7、服务端Stub程序将程序调用结果按照约定的协议进行序列化,并通过网络发送回客户端Stub程序。
8、客户端Stub程序接收到服务端Stub发送的返回数据,对数据进行反序列化操作,并将调用返回的数据传递给客户端请求发起者。
9、客户端请求发起者得到调用结果,整个RPC调用过程结束。
RPC涉及到的相关技术 通过上文一系列的文字描述和讲解,我们已经了解了RPC的由来和RPC整个调用过程。我们可以看到RPC是一系列操作的集合,其中涉及到很多对数据的操作,以及网络通信。因此,我们对RPC中涉及到的技术做一个总结和分析:
1、动态代理技术: 上文中我们提到的Client Stub和Sever Stub程序,在具体的编码和开发实践过程中,都是使用动态代理技术自动生成的一段程序。
2、序列化和反序列化: 在RPC调用的过程中,我们可以看到数据需要在一台机器上传输到另外一台机器上。在互联网上,所有的数据都是以字节的形式进行传输的。而我们在编程的过程中,往往都是使用数据对象,因此想要在网络上将数据对象和相关变量进行传输,就需要对数据对象做序列化和反序列化的操作。
序列化:把对象转换为字节序列的过程称为对象的序列化,也就是编码的过程。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化,也就是解码的过程。
服务定义及暴漏:
func (t *T) MethodName(request T1,response *T2) error 上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是: 1、对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。 2、方法的第二个参数必须是指针类型。 3、方法的返回类型为error。 4、方法的类型是可输出的。 * 5、方法本身也是可输出的。
type MathUtil struct{}//该方法向外暴露:提供计算圆形面积的服务 func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error { *resp = math.Pi * req * req //圆形的面积 s = π * r * r return nil //返回类型 }
代码讲解: 在上述的案例中,我们可以看到: 1、Calculate方法是服务对象MathUtil向外提供的服务方法,该方法用于接收传入的圆形半径数据,计算圆形面积并返回。 2、第一个参数req代表的是调用者(client)传递提供的参数。 3、第二个参数resp代表要返回给调用者的计算结果,必须是指针类型。 4、正常情况下,方法的返回值为是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) }
看完上述内容,你们对Golang中怎么实现一个RPC功能有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。
当前标题:Golang中怎么实现一个RPC功能
文章地址:http://pwwzsj.com/article/jgsjej.html