protobuf 介绍,Python 和 Go 编写 rpc 服务(gRPC)

rpc 指的是远程过程调用(Remote Procedure Call),简单理解就是一个节点请求另一个节点提供的服务。

假设有两台服务器 A B,一个部署在 A 服务器上的应用,想要调用 B 服务器上某个应用提供的函数 / 方法。但由于不在同一个内存空间,所以不能直接调用,而是需要通过网络来表达调用的语义传达调用的数据。

显然与 rpc 对应的则是本地过程调用,我们本地调用一个函数便是最常见的本地过程调用。

但是很明显,将本地过程调用变成远程过程调用会面临各种各样的问题。

我们以一个简单的本地过程调用(Python 函数)为例:

def add(a, b): return a + b total = add(1, 2)

我们调用了 add 函数,查看它的字节码的话,会发现分为以下几步:

1. LOAD_NAME: 加载变量 add 指向的函数对象

2. LOAD_CONST: 将 1 和 2 两个整数压入运行时栈

3. CALL_FUNCTION: 进行函数调用,将函数返回值保存在栈顶

4. STORE_NAME: 从栈顶弹出返回值,赋值给 total

当然我们这里不是为了介绍函数的执行过程,只是为了表明在本地调用一个函数是极其简单的,但如果是远程过程调用就不一样了。假设我们上面的 add 函数部署在另一个节点上,那么我们在本地要如何去调用呢?显然这么做的话,我们需要面临如下问题:

1. Call 的 id 映射

远程服务中肯定不止一个函数,那么我们要怎么告诉远程机器,我们调用的是 add 函数,而不是 sub 或者其它的函数呢?首先在本地调用中,我们直接通过函数指针即可,编译器或解释器会自动帮我们找到指针指向的函数。但是在远程调用中是不行的,因为它们不在同一个节点,自然更不在同一进程,而两个进程的地址空间是不一样的。所以在 rpc 中,每个函数必须都有一个唯一的 ID,客户端在远程过程调用时,必须要附上这个 ID。然后客户端和服务端还需要各自维护一个 "函数和 Call id 之间的映射关系",相同的函数对应的 Call id 必须一致。当客户端需要进程远程调用时,根据映射关系找到函数对应的 Call  id,传递给服务端;然后服务端再根据 Call id 找到要调用的函数,然后进行调用。

2. 序列化和反序列化

这个相信你很熟悉,在做 web 开发的时候我们经常会用到。比如 Python 编写的 web 服务返回一个字典,那么它要如何变成 Go 的 map 呢?显然是先将 Python 的字典序列化成 json,然后 Go 再将 json 反序列化成 map。而 json 便是两者之间的媒介,它是一种数据格式,也是一种协议。这在 rpc 中也是同理,因为是远程调用,那么必然要涉及的数据的传输。那么问题来了,我们调用的时候肯定是需要传递参数的,那么这些参数要怎么传递呢?而且客户端和服务端使用的语言也是可以不一样的,比如客户端使用 Python,服务端使用 C++、Java 等等,而不同语言对应的数据结构不同,例如我们不可能在 C++、Java 里面操作 Python 中的字典、类实例等等。

所以还是协议,这是显而易见、而且最直接的解决办法。我们在传递参数的时候可以将内存中的对象序列化成一个可以在网络中传输的二进制对象,这个对象不是某个语言独有的,而是大家都认识。然后传输之后,服务端再将这个对象反序列化成对应语言的数据结构,同理服务端返回内容给客户端也是相同的过程。所以我们还是想到了 http + json,因为 它们用的太广泛了,客户端发送 http 请求,通过 json 传递参数;然后服务端处理来自客户端的请求,并将传递的 json 反序列化成对应的数据结构,并执行相应的逻辑;执行完毕之后,再将返回的结果也序列化成 json 交给客户端,客户端再将其反序列化。显然这是一个非常非常非常通用的流程,而实现了 rpc 的框架(gRPC)也是同样的套路,只不过它没有采用 http + json 的方式,因为这种协议是非常松散的,至于 gRPC 到底用的是什么协议我们后面说。

3. 网络传输

因为是远程调用,那么必然涉及到网络的传输,因此就需要有一个网络传输层。网络传输层需要把 Call id 和序列化的参数字节流返回传递给服务端,服务端逻辑执行完毕之后再将结果序列化并返回给客户端。只要能完成这个过程,那么都可以作为传输层使用。因此 rpc 所使用的协议是可以有多种的,只要能完成传输即可,尽管大部分 rpc 框架使用的都是 TCP 协议,但其实 UDP 也可以,而 gRPC 则直接使用了 HTTP2。另外,Java 的 Netty 也属于这层的东西。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwfsgj.html