成熟的技术
gRPC 不仅仅是一个新颖的框架,它是一个经过大规模实战检验的框架,已经被 Google 使用了十多年。最初,这个项目是为内部使用而开发的,但在 2016 年,Google 决定提供一个开源版本,它不再依赖于公司内部的工具和架构的特定要求。
此后,像 Uber 这样的公司,以及许多其他公司,迁移了他们现有的服务到 gRPC,既为了效率,也为了它提供的额外特性。此外,一些开源项目,例如 etcd(一种分布式键值存储,Kubernetes 核心使用它)也使用 gRPC 来进行多个实例间的通信。
最近,Microsoft 加入了构建 .NET
版本的 gRPC 的努力。虽然本书的目标并不是解释它做了什么,但这显然表明了对这个项目的关注。此外,像 Microsoft 这样的公司愿意贡献更多资源,意味着社区和工具的资源会越来越多,对我们来说这是一个好消息。
现在,虽然这些成就听起来令人兴奋,但我意识到我们中的大多数人可能不会达到这些大公司的规模,因此理解 gRPC 擅长的地方是很重要的。接下来,我们来看一些它表现出色的用例。
第一个被广泛讨论的用例是微服务之间的通信。对于多语言微服务来说,这个用例尤其具有吸引力。我们作为软件工程师的任务是选择合适的工具来处理不同的任务,而代码生成可以让我们在不同的语言中做到这一点。
另一个用例是实时更新。正如我们所看到的,gRPC 让我们能够进行数据流传输,提供了多种流模式,例如服务器流传输,这对于保持实时数据更新(如股票价格)非常有用。接着是客户端流传输,适用于传感器将数据流式传输到后端。最后是双向流传输,适用于客户端和服务器都需要实时了解对方更新的情况,例如即时通讯应用。
另一个重要的用例是进程间通信(IPC)。这发生在同一台机器上的不同进程之间。它有助于同步两个或多个独立的应用程序,实现模块化架构中的关注点分离(SOC),或者通过应用沙箱提高安全性。
显然,我在这里介绍的是 gRPC 最常见的应用场景,但它还有许多其他的应用。重要的是,你需要在自己的用例中进行测试,看看它是否满足你的需求。如果你对测试 gRPC 感兴趣,首先需要尝试了解 Protobuf
如何减少你的负载和提高应用效率。
为什么选择 Protobuf?
到现在为止,你应该已经明白,Protobuf
提供了一种编写数据 schema 的方式,描述了我们如何序列化和反序列化数据。然后,Protobuf
编译器(protoc
)让我们可以从这些 schema 生成一些代码,用来在代码中使用生成的类型,而这些可序列化的类型正是 gRPC 用来让用户代码与请求和响应对象进行交互,并让 gRPC 框架将它们的二进制表示通过网络传输。
消息的二进制表示是 Protobuf
作为 gRPC 默认数据 schema 的最大理由。与传统的数据 schema(如 XML
、JSON
等)相比,Protobuf
序列化的数据占用的字节数要少得多。这意味着,不仅消息可以更快地传输,而且反序列化的速度也更快。
以下实验主要是为了展示 要获取数据,你需要先解压
然后,为了运行实验,你首先需要使用
编译完成后,可以通过运行以下命令来执行 Go 代码:
该命令会在 |
为了演示这一点,我们可以做一个简单的实验——我们可以生成 100,000 个账户(包含 ID 和用户名),反序列化 1,000 次,并计算反序列化所有数据所需的平均时间。以下是一次实验的结果,其中 JSON 数据包含换行和空格,Protobuf
使用二进制格式:
JSON: 49.773ms
PB: 9.995ms
然而,大多数开发者会去除 JSON 中的换行和空格,下面是去除换行和空格后的结果:
JSON: 38.692ms
PB: 9.712ms
虽然去除换行和空格后 JSON
的性能有所提升,但它仍然显著慢于 Protobuf
。
最后,我们来看一下实验中使用的数据的序列化数据大小。对于未经压缩的 JSON
和 Protobuf
数据,我们得到以下输出:
1.3M accounts.bin
3.1M accounts.json
对于压缩后的版本(gzip
),输出如下:
571K accounts.bin.gz
650K accounts.json.gz
我鼓励你对这个进行更多实验,尤其是在你的实际用例中进行测试,但除非在 proto
文件设计上有很大的问题,否则你会发现 Protobuf
在数据大小和序列化/反序列化时间方面远远更高效。
除了提供数据序列化外,我们还看到 Protobuf
具有服务的概念,这是客户端和服务器之间的契约。虽然这个概念并不完全与 gRPC 绑定(你可以为其他框架生成代码),但 gRPC 使用它来生成适当的 API 端点。这为我们提供了客户端和服务器端的类型安全。如果我们尝试发送不正确的数据,并且我们使用的是编译语言,编译时就会出现错误,而不是在运行时才发现错误。这大大缩短了开发者的反馈周期,并减少了代码中可能出现的错误区域。
最后,Protobuf
本身是语言无关的。这意味着它是一个独立的数据架构,可以在多个项目之间共享。如果你有一个用 C++ 编写的代码(例如在某个微控制器上),它向用 Go 编写的后台发送数据,而后台又将数据发送到用 JS 编写的 Web 前端,你只需共享相同的 Protobuf
文件,并使用 protoc
生成模型。你不需要每次在不同的项目中重写它们。这减少了在添加或更新功能时需要更新的部分,并为多个团队提供了一个需要达成一致的接口。
最终,Protobuf
通过创建更小的负载来实现更快的通信(例如通过 gRPC),并为通信双方提供了类型安全,最重要的是,它可以跨多个语言使用,这样我们就可以为不同的任务选择合适的工具。