在第一部分,我们学习了gRPC的基本调用过程,这样我们对全局层面有了一定了解。接下来,我们将结合官方文档,继续深入学习、探索下去。
1. Authentication 认证的实现
示例很简单,客户端和服务端都大致分为两步:
- 生成对应的认证信息
creds - 将认证信息作为
DialOption传入信息
认证方法的底层实现并不在我们今天的讨论范围内。这里值得一提的是,由于请求会存在大量的输入参数,这里提供的方法是 opts ...DialOption,也就是可变长度的参数,这一点很值得我们思考和学习。
客户端的认证实现
第一步:将认证信息放入连接中
grpc.WithTransportCredentials中,将creds保存到copts.TransportCredentials- 调用
Dial,在内部用opt.apply(&cc.dopts)将认证信息传递到结构中 credsClone = creds.Clone()使用了一份复制,放到了Balancer中,估计是用于负载均衡的,暂时不用考虑
第二步:将认证信息请求中发出
- 首先我们先找到
Invoke函数,这里是发送请求的入口(对这一块有疑问的,查看上一篇) - 分析一下函数
invoke,调用了newClientStream,一大段代码都没有用到copts.TransportCredentials中的参数,大致猜测是在clientStream中 - 接下来这块,只通过阅读代码,要找到对应使用到
copts.TransportCredentials很麻烦,建议第一次可以先通过反向查找,调用到这个参数的地方 newHTTP2Client=>NewClientTransport=>createTransport=>tryAllAddrs=>resetTransport=>connect=>getReadyTransport=>pick=>getTransport=>newAttemptLocked=>newAttemptLocked=>newClientStream- 这时,我们再正向梳理一下其调用逻辑,大致是查找连接情况,对传输层进行初始化。如果你了解认证是基于传输层
Transport的,那下次正向查找时,会有一条比较明确的方向了
服务端的认证实现
第一步:将认证信息放入Server结构中
- 将
creds包装成ServerOption,传入NewServer中 - 类似Client中的操作,被存至
opts.creds里
第二步:在连接中进行认证
- 参考之前一讲的分析,我们进入函数
handleRawConn - 这次,我们的进展很顺利,一下子就看到了关键函数名
useTransportAuthenticator - 在这里,调用了
creds实现的ServerHandshake实现了认证。到这里,认证已经完成,不过我们可以再看看,认证信息是怎么传递的 - 接着,认证信息传入了
newHTTP2Transport,保存到结构体http2Server中的authInfo,最后返回了一个InterfaceServerTransport - 在进行连接时,调用了
serveStreams,然后调用了http2Server的HandleStreams方法,这时,我们大致可以猜测,auth在这里被用到了 - 往下看,发现有个对header帧的处理
operateHeaders,在这里被赋值到pr.AuthInfo里,并被保存到s的Context中 - 一般情况下,Context的调用是十分隐蔽的,我们可以通过反向查找,哪里调用了
peer.FromContext,然而并没有地方应用,那认证的分析,就告一段落了
2. 四类gRPC调用的实现
这一块我们暂不深入源码,先了解使用时的特性
2.1 简单RPC
代码逻辑很直观,即处理后返回
2.2 服务端流式RPC
代码的关键在于两个函数inRange和 stream.Send
2.3 客户端流式RPC
用一个for循环进行多次发送,stream.Recv()实现了从服务端获取数据,当EOF时,才调用stream.SendAndClose结束发送
2.4 双向流式RPC
将 SendAndClose 变为 Send,其余基本不变。从这里可以看到,正常的关闭都是由服务端发起的。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili:https://space.bilibili.com/293775192
公众号:golangcoding