Service定义
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
客户端代码
...
func main() {
conn, ... := grpc.Dial(address, ...)
c := pb.NewGreeterClient(conn)
c.SayHello(..., &pb.HelloRequest{Name: name})
}
grpc.Dial()返回了一个grpc.ClientConn对象(一个抽象的网络连接)conn,然后将conn注入到helloworld.greeterClient对象(由proto文件生成)c,最终通过c发起一次RPC调用SayHello。
一次RPC调用经过了哪些代码层次?
helloworld.greeterClient.SayHello()只是对grpc.ClientConn.Invoke()的简单包装,指定了HelloRequest,HelloReply为输入输出参数。
每次的grpc.ClientConn.Invoke()调用,过程中会创建一个临时的grpc.clientStream对象,然后依次调用grpc.clientStream.SendMsg()、grpc.clientStream.RecvMsg()便完成了一次RPC调用,随后grpc.clientStream对象的生命周期结束。
grpc.ClientConn层次之下是grpc.csAttempt,含义为一次RPC调用的“意图”,grpc.csAttempt这一层次的封装主要负责RPC的失败重试。每次通过grpc.csAttempt对象发起RPC通信,如若RPC通信失败,则会重新构建grpc.csAttempt(重新获取下层网络连接),然后进行重试。
grpc.clientStream.SendMsg()实际上是调用grpc.csAttempt.sendMsg(),外加失败重试处理。近似的grpc.clientStream.RecvMsg()对应grpc.csAttempt.recvMsg()。
grpc.csAttempt层次之下是transport.http2Client和transport.Stream,transport.Stream负责对transport.http2Client的分流处理。transport.http2Client可被多个RPC Client复用,读写数据依据stream id分发到不同的transport.Stream。
最后transport.http2Client之下是http2协议http2.Framer这里不做赘述。
调用层次树状图
helloworld.greeterClient.SayHello()
grpc.ClientConn.Invoke()
grpc.clientStream = grpc.newClientStream()
grpc.csAttempt = grpc.clientStream.newAttemptLocked()
transport.http2Client = transport.newHTTP2Client()
http2.Framer = http2.NewFramer()
transport.Stream = transport.http2Client.NewStream()
grpc.clientStream.SendMsg()
grpc.csAttempt.sendMsg()
transport.http2Client.Write(transport.Stream)
http2.Framer.w.Write()
grpc.clientStream.RecvMsg()
grpc.csAttempt.recvMsg()
transport.Stream.Read()
http2.Framer.r.Read()
