美文网首页GoGo语言实践
Go语言中的RPC编程

Go语言中的RPC编程

作者: 帅气的昵称都有人用了 | 来源:发表于2019-04-24 09:16 被阅读3次

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

上面就是关于RPC定义的介绍,其实RPC就是想实现函数调用模式的网络化。客户端就可以像调用本地函数一样,然后客户端把这些参数打包后通过网络传递到服务器端,服务器端解包到处理过程中执行,然后执行的结果反馈给客户端。

具体来说,在运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:

1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据**

Go RPC

Go在标准库中已经支持了RPC,而且支持三个级别的RPC:TCP,HTTP,JSONRPC。但Go中的RPC又都是通过Gob来统一编码的,因此它只能支持在Go语言开发的服务器和客户端之间的交互。
在被远程访问之前,还需要满足一些条件:

1)函数必须是导出的(首字母大写)
2)必须有两个导出类型的参数
3)第一个参数用来接收,第二个参数是返回给客户端的参数,并且第二个参数必须是指针类型
4)函数还要有一个返回值error

格式如下:

func (t *T) MethodName(arg1 argType, arg2 *argType) error

HTTP RPC

我们来看一个简单的HTTP的服务器端代码:

package main

import (
    "errors"
    "log"
    "net/http"
    "net/rpc"
    "os"
)

type Args struct {
    A, B int
}

type Quotient struct {
    Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("The divisor cannot be zero.")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

func main() {
    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()

    err := http.ListenAndServe(":8000", nil)
    if err != nil {
        log.Fatalln(err.Error())
        os.Exit(1)
    }
}

我们首先注册了一个Arith的RPC的服务,然后通过rpc.HandleHTTP函数把该服务注册到HTTP协议上,最后就可以利用HTTP的方式来传递数据。
我们再来看看客户端代码:

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ", os.Args[0], "server")
        os.Exit(1)
    }
    serverAddress := os.Args[1]

    client, err := rpc.DialHTTP("tcp", serverAddress+":8000")
    if err != nil {
        log.Fatal("Dial Wrong: ",err)
    }
    args := Args{17, 8}
    var reply int
    err = client.Call("Arith.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Arith Wrong: ", err)
    }
    fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, &reply)

    var quot Quotient
    err = client.Call("Arith.Divide", args, &quot)
    if err != nil {
        log.Fatal("Arith Wrong: ", err)
    }
    fmt.Printf("Arith: %d/%d=%d and %d\n", args.A, args.B, quot.Quo, quot.Rem)
}

程序中的关键就在于这个Call函数,它有三个参数,第一个是要调用的函数的名字,第二个要传递的参数,第三个是要返回的参数(必须是指针类型


TCP RPC

我们仍使用前面的内容来构建基于TCP的RPC的服务器端:

func main() {
    arith := new(Arith)
    rpc.Register(arith)

    tcpAddr, err := net.ResolveTCPAddr("tcp", "8000")
    checkError(err)
    
    listener, err := net.Listen("tcp", tcpAddr)
    checkError(err)
    
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        rpc.ServeConn(conn)
    }
}

func checkError(err error) {
    if err != nil {
        fmt.Println("Wrong: ", err.Error())
        os.Exit(1)
    }
}

在这里使用了TCP协议,然后需要自己连接控制,当有客户端连接上来后,需要把这个连接交给RPC来处理。
相应的RPC客户端代码如下:

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ", os.Args[0], "server:port")
        os.Exit(1)
    }
    server := os.Args[1]
    client, err := rpc.Dial("tcp", server)
    if err != nil {
        log.Fatal("Dial Wrong: ", err)
    }
    args := Args{17, 8}
    var reply int
    err = client.Call("Arith.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Arith Wrong: ", err)
    }
    fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, &reply)

    var quot Quotient
    err = client.Call("Arith.Divide", args, &quot)
    if err != nil {
        log.Fatal("Arith Wrong: ", err)
    }
    fmt.Printf("Arith: %d/%d=%d and %d\n", args.A, args.B, quot.Quo, quot.Rem)
}

和HTTP的客户端代码相比,唯一的区别就在于这里使用的是Dial(tcp)函数,而前者使用的是DialHTTP


JSON RPC

JSON RPC采用的是json编码的方式,但其操作方式是与上面介绍的RPC方式是基本一致的,和TCP所创建的客户端和服务器端唯一的不同在于,JSON RPC在这里是用的连接方式是jsonrpc.ServerConn(conn)其余的内容完全一致。
代码在这里不再赘述。


RPC接口

Go语言的net/rpc很灵活,它在传输前后实现了编码解码器的接口定义。
RPC提供的编码解码器接口如下:

type ClientCodec interface {
    WriteRequest(*Request, interface{}) error
    ReadResponseHeader(*Response) error
    ReadResponseBody(interface{}) errror
    Close() error
}

type ServerCodes interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    WriteResponse(*Request, interface{}) error
    Close() error
}

我们可以通过定义很容易的看出来两个编码器的运行过程。
接口ClientCodec定义了RPC客户端如何在一个RPC会话中发送请求和读取响应。
接口ServerCodec定义了RPC服务器端如何在一个RPC会话中接收请求并发送响应。

相关文章

  • Go语言中的RPC编程

    RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而...

  • 【golang微服务】RPC原理与Go RPC

    本文介绍了RPC的概念以及Go语言中标准库rpc的基本使用。 什么是RPC RPC(Remote Procedur...

  • golang中的net/rpc包

    本文先介绍RPC,然后go原生对RPC的使用,之后是介绍go语言中有哪些RPC框架以及一些其他常见的框架,最后是探...

  • go 语言中 RPC

    微服务已经 hot 了一段时间,自己作为 web 开发人员当然也不由自主想研究研究微服务,不过微服务的整个知识体系...

  • 89.go微服务之jsonRPC实践

    go语言中的RPC只能在go语言的程序中调用。如果使用其他语言编写的客户端,就要用到jsonRPC了。jsonRP...

  • TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语...

  • Go语言入门指南系列之《2基础类型》

    Go 语言中的基础类型   在 Go 编程语言中,数据类型用于声明函数和属性。数据类型的出现时为了把数据分成所需要...

  • 【Golang 基础】Go 语言中的基本类型

    Go 语言中的基础类型   在 Go 编程语言中,数据类型用于声明函数和属性。数据类型的出现时为了把数据分成所需要...

  • Go并发

    Go语言中的并发编程 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很...

  • go 学习笔记之10 分钟简要理解 go 语言闭包技术

    闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍 Go 语言中什么是闭包以及怎么理...

网友评论

    本文标题:Go语言中的RPC编程

    本文链接:https://www.haomeiwen.com/subject/zmxwgqtx.html