Remote Procedure Call(rpc)

rpc

Posted by cslqm on January 20, 2021

都是基于 Go 描述的。

RPC是远程过程调用的缩写(Remote Procedure Call),通俗地说就是调用远处的一个函数。远处到底有多远呢?可能是同一个文件内的不同函数,也可能是同一个机器的另一个进程的函数,还可能是远在火星好奇号上面的某个秘密方法。

而Protobuf因为支持多种不同的语言(甚至不支持的语言也可以扩展支持),其本身特性也非常方便描述服务的接口(也就是方法列表),因此非常适合作为RPC世界的接口交流语言。

Go 语言的 RPC 包,路径为 net/rpc。

Go RPC 举例

抄袭自 Go 语言高级编程。

  1. 先定义个可以被调用的方法
    1
    2
    3
    4
    5
    
    type HelloService struct {}
    func (p *HelloService) Hello(request string, reply *string) error {
     *reply = "hello:" + request
     return nil
    }
    

    go 的 RPC 规则:方法只能由两个可序列化的参数,第二个是指针,必须返回 error,方法必须是公开的。

  2. 将 HelloService 类型的对象,注册为一个 RPC 服务,用来被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    listener, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("ListenTCP error:", err)
    }

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("Accept error", err)
    }
    
    rpc.ServeConn(conn)
}
  1. 写一个客户端请求 rpc 服务的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
    client, err := rpc.Dial("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }

    var reply string
    err = client.Call("HelloService.Hello", "hello", &reply)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(reply)
}

rpc 用在 http 上

Go 已经让自己的 RPC 在 http 协议上提供 RPC 服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
        var conn io.ReadWriteCloser = struct {
            io.Writer
            io.ReadCloser
        }{
            ReadCloser: r.Body,
            Writer:     w,
        }

        rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
    })

    http.ListenAndServe(":1234", nil)
}

RPC的服务架设在“/jsonrpc”路径,在处理函数中基于http.ResponseWriter和http.Request类型的参数构造一个io.ReadWriteCloser类型的conn通道。然后基于conn构建针对服务端的json编码解码器。最后通过rpc.ServeRequest函数为每次请求处理一次RPC方法调用。

一次调用请求

1
2
curl localhost:1234/jsonrpc -X POST \
    --data '{"method":"HelloService.Hello","params":["hello"],"id":0}'