成熟的技术
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),并为通信双方提供了类型安全,最重要的是,它可以跨多个语言使用,这样我们就可以为不同的任务选择合适的工具。