对gRPC实践后,可以跟着调用链路分析下gRPC的调用流程。
这里抛几个问题:
- gRPC调用经历了哪些过程?
- gRPC内部调用的实现是什么样?
- gRPC底层传输依赖什么协议?
- client端如何发起的请求?
- server如何接收的请求?
4.1、探索客户端
4.1.1、client的生成
以下是demo里面的client实现,生成一个client,然后发起SayHello请求。
1 | func main() { |
client的构建依赖以下func。
1 | func NewGreeterClient() helloworld.GreeterClient { |
helloworld.GreeterClient是通过protoc生成的stub代码。如下:
1 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. |
具体的client是通过helloworld.NewGreeterClient(conn)生成的,他的实现如下:
1 | func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { |
在桩代码中不难发现,greeterClient结构体就是GreeterClient的实现:
1 | func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { |
4.1.2、SayHello的发起
执行client.SayHello(ctx, req),发起greeterClient的SayHello的调用,实现如下:
1 | func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { |
整体的调用流程如下:
进入Invoke,Invoke主要实现请求参数的整合与拦截器的调用,实现如下:
1 | func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error { |
无论拦截器是否为空,最终都会调用invoke,invoke中具体实现客户端流的创建,发送数据,接收数据。
1 | func invoke(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error { |
4.1.2.1、SendMsg发送数据
实现流程如下
1 | func (cs *clientStream) SendMsg(m interface{}) (err error) { |
4.1.2.2、RecvMsg接收数据
1 | func (cs *clientStream) RecvMsg(m interface{}) error { |
4.2、服务端探索
Server开启后,会等待连接请求,实现如下(省略其他代码),当接收到请求,开启一个goroutine处理该连接。
1 | for { |
4.2.1、handleRawConn
handleRawConn中主要设置连接超时时间,鉴权,建立HTTP2的连接。请求处理。
实现如下:
1 | func (s *Server) handleRawConn(rawConn net.Conn) { |
4.2.2、serveStreams
serveStreams的处理如下:
1 | func (s *Server) serveStreams(st transport.ServerTransport) { |
4.2.3、HandleStreams
HandleStreams循环处理客户端的请求数据,根据frame执行不同的方法。MetaHeadersFrame类型就是执行具体的业务实现。traceCtx将跟踪附加到ctx并返回新的上下文。
大致流程如下:
1 | func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) { |
4.2.4、handleStream
最后回调执行handleStream,它会解析函数描述,执行注册的业务接口大致执行如下:
1 | func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream, trInfo *traceInfo) |
4.3、总结
通篇主要是讲解gRPC的请求过程,具体的比如服务的创建,连接。编解码,http2的创建,这些细节都没有详细描述,后面有机会在具体探讨。这次主要是了解大体流程。
1 | "personID":"1551181497964383" |