Golang中怎么实现一个RPC功能

发布时间:2021-07-06 14:57:24 作者:Leah
来源:亿速云 阅读:179

今天就跟大家聊聊有关Golang中怎么实现一个RPC功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

客户端:服务调用发起方,又称之为服务消费者 服务器:远端计算机上运行的程序,其中包含客户端要调用和访问的方法 客户端存根:存放服务器端的地址,端口消息,将客户端的请求参数打包成网络消息,发送给服务器方。接收服务器方返回的数据包。该段程序运行在客户端。 服务端存根:接收客户端发送的数据包,解析数据包,调用数据包,调用具体的服务方法,将调用结果打包发送给客户端一方。该段程序运行在服务端。

工作过程:

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功能有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

推荐阅读:
  1. 怎么在golang中实现一个redis延时消息队列功能
  2. net与rpc包怎么在golang中使用

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

golang rpc

上一篇:如何实现jQuery添加删除按钮Click事件

下一篇:JavaScript如何实现评论点赞功能

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》