都是基于 Go 描述的。
RPC是远程过程调用的缩写(Remote Procedure Call),通俗地说就是调用远处的一个函数。远处到底有多远呢?可能是同一个文件内的不同函数,也可能是同一个机器的另一个进程的函数,还可能是远在火星好奇号上面的某个秘密方法。
而Protobuf因为支持多种不同的语言(甚至不支持的语言也可以扩展支持),其本身特性也非常方便描述服务的接口(也就是方法列表),因此非常适合作为RPC世界的接口交流语言。
Go 语言的 RPC 包,路径为 net/rpc。
Go RPC 举例
抄袭自 Go 语言高级编程。
- 先定义个可以被调用的方法
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,方法必须是公开的。
- 将 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)
}
- 写一个客户端请求 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}'