这一讲我们将以一个实战案例,基于前两讲提到的 HTTP/2 和 ProtoBuf 协议,看看 gRPC 如何将结构化消息编码为网络报文。
直接操作网络协议编程,容易让业务开发过程陷入复杂的网络处理细节。RPC 框架以编程语言中的本地函数调用形式,向应用开发者提供网络访问能力,这既封装了消息的编解码,也通过线程模型封装了多路复用,对业务开发很友好。
其中,Google 推出的 gRPC 是性能最好的 RPC 框架之一,它支持 Java、JavaScript、Python、GoLang、C++、Object-C、Android、Ruby 等多种编程语言,还支持安全验证等特性,得到了广泛的应用,比如微服务中的 Envoy、分布式机器学习中的 TensorFlow,甚至华为去年推出重构互联网的 New IP 技术,都使用了 gRPC 框架。
然而,网络上教你使用 gRPC 框架的教程很多,却很少去谈 gRPC 是如何编码消息的。这样,一旦在大型分布式系统中出现疑难杂症,需要通过网络报文去定位问题发生在哪个系统、主机、进程中时,你就会毫无头绪。即使我们掌握了 HTTP/2 和 Protobuf 协议,但若不清楚 gRPC 的编码规则,还是无法分析抓取到的 gRPC 报文。而且,gRPC 支持单向、双向的流式 RPC 调用,编程相对复杂一些,定位流式 RPC 调用引发的 bug 时,更需要我们掌握 gRPC 的编码原理。
这一讲,就将以 gRPC 官方提供的 example:data_transmisstion 为例,介绍 gRPC 的编码流程。在这一过程中,会顺带回顾 HTTP/2 和 Protobuf 协议,加深对它们的理解。虽然这个示例使用的是 Python 语言,但基于 gRPC 框架,你可以轻松地将它们转换为其他编程语言。
如何使用 gRPC 框架实现远程调用?
我们先来简单地看下 gRPC 框架到底是什么。RPC 的全称是 Remote Procedure Call,即远程过程调用,它通过本地函数调用,封装了跨网络、跨平台、跨语言的服务访问,大大简化了应用层编程。其中,函数的入参是请求,而函数的返回值则是响应。
gRPC 就是一种 RPC 框架,在你定义好消息格式后,针对你选择的编程语言,gRPC 为客户端生成发起 RPC 请求的 Stub 类,以及为服务器生成处理 RPC 请求的 Service 类(服务器只需要继承、实现类中处理请求的函数即可)。如下图所示,很明显,gRPC 主要服务于面向对象的编程语言。
gRPC 支持 QUIC、HTTP/1 等多种协议,但鉴于 HTTP/2 协议性能好,应用场景又广泛,因此 HTTP/2 是 gRPC 的默认传输协议。gRPC 也支持 JSON 编码格式,但在忽略编码细节的 RPC 调用中,高效的 Protobuf 才是最佳选择!因此,这一讲仅基于 HT