我们知道RPC框架是一个CS的架构,有服务的提供者,有服务的消费者,那么一次RPC请求到底经历也什么了?这篇文章一起从源码揭秘gRPC的一次请求生命周期,从其中我们探寻RPC框架设计时一些必要的模块,进行抽象总结。
文章较长,希望大家有耐心。
在看客户端如何发起一次请求时,我们先看看pb文件,和生成的pb.go文件,事实上常规的rpc请求和流式的rpc请求是不一样的,这里我们主要分析常规的rpc请求(也就是一次请求,一次响应)
通过protobuffer工具生成pb.go文件,这个文件中包含的信息比较多,这里我们先主要看对HelloService服务的描述信息
我们从HelloWorld的RPC请求看起,看看这个一次请求,一次响应是怎么执行的,首先在pb.go文件中,我们看到客户端使用的api的定义,如下代码
这个HelloWorld方法接受三个参数:
这里着重说一下第三个参数,前两个参数相信大家都知道是什么意思,看gRPC中对这个参数的定义和描述,很清楚的知道这是一个接口,接口中定义了before方法和after方法,看如下注释很容易明白的,我们可以自定义结构体实现自己想要处理的一些逻辑。
接下来我们看看客户端api的实现,也是在pb.go文件中,核心是Invoke方法,
当我们在代码中发起调用时,像如下代码一样传入参数,第三个参数我们可以传入一个空的 CallOption
,这是grpc提供的默认实现,这个实现在rpc_util.go文件中。事实上,grpc提供了很多默认实现,都在这个文件中,这不是本次的重点,就不展开说了
最后我们深入invoke方法中做了什么,invoke方法在call.go文件中
在invoke方法中主要做了如下如下事情
我们进入到SendMsg中看看消息是如何发送出去的
我们再进入RecvMsg中看看客户端是如何接受消息的
在之前的文章gRPC-Server启动做了哪些事,详细分析了gRPCServer的启动流程,这篇文章我们接着看看服务端监听到一个客户端连接之后,是如何处理这个请求的。
在 grpc.Server(listener)
中有如下片段代码
我们主要分析的是在handleRwConn方法中做了哪些事
继续深入ServerStreams()方法看看是如何处理客户端请求的
HandleStream方法中主要是循环读取http2协议发送的各种帧,然后交给不同的方法去处理,其中MetaHeadersFrame帧会触发调用服务端的服务实现,traceCtx主要负责跟踪执行过程。这里省略很多代码,感兴趣的去阅读源码,文章里就不粘贴了。
最后我们看看这个真正调用我们自己业务服务代码的方法是做了什么,省略很多非核心的代码,这样流程比较清晰 s.handleStream(st,stream,s.traceInfo(st,stream))
深入阅读进去,你会发现源码并不是特别难懂,关键在于踏出第一步,上面分析了grpc从客户端发起请求到服务端接受处理的全流程,中间也有很多细节并没有说,比如鉴权,比如创建http2服务,拦截器执行,trace跟踪等,尤其是错误处理,但本篇文章重点是带领大家贯穿整个流程,把从客户端发起请求到服务端处理衔接起来,并不是把所有细节说明白,一篇文章也说不明白,最后我用一张图表述整个流程,让大家更加清晰的理解。