在之前的文章中,我们全面介绍了gRPC,在这一部分中,我们将涵盖Protocol Buffer,也称为Protobuf。
Protobuf标志
“Protocol Buffers”这个名字有着独特的起源。
在早期,它指的是一个名为“ProtocolBuffer”的类,充当了单个方法调用的缓冲区。用户可以向此缓冲区添加标签/值对,原始字节会存储在其中,直到构建消息后被写出。尽管名称中的“buffers”部分失去了原始含义,但它一直存在。今天,我们通常使用“协议消息”来指代抽象意义上的消息,“协议缓冲区”来指代序列化消息,以及“协议消息对象”来指代解析后的内存表示。
什么是Protocol Buffers?
Protocol Buffers是一种简单的语言中立和平台中立的接口定义语言(IDL),用于定义数据结构模式和编程接口。它支持二进制和文本线路格式,并可以与不同平台上的许多不同线路协议一起工作。例如,看看这个简单的proto文件(person.proto),定义了一个名为'Person'的消息。这个消息描述了一个人的属性,包括名字,ID和可选的电子邮件地址。message Person { required string name = 1; required int32 id = 2; optional string email = 3; } 这个person.proto文件用作服务器和客户端之间的契约。如果您想要更改这个“Person”实体的结构,或者更改请求和响应的外观,您需要修改proto文件。Protobuf编译器protoc由Google维护,尽管也有可选的实现。生成的代码经过优化,以实现数据的快速序列化和反序列化。
为什么选择Protocol Buffers(Protobuf)而不是JSON?
Proto vs JSON
你可能会想,既然已经有广泛使用的序列化格式JSON,为什么要选择Protocol Buffers(Protobuf)呢?
让我们深入探讨Protobuf为什么是一个出色的选择,以及它在解决常见数据序列化挑战方面与JSON相比的情况:
在总结一下,Protobuf和JSON各有各自的独特优势,Protobuf在需要效率、跨平台兼容性和结构化数据至关重要的情况下表现出色。
另一方面,当您需要可读性强的数据或轻量级格式的简单性更合适时,JSON仍然是一个很好的选择。
Protobuf语法
这个快速介绍为您提供了Protobuf的语法和核心概念的味道。如果您想进一步探索,我鼓励您查看官方Protocol Buffers文档。
1.消息:数据蓝图
将Protobuf消息视为数据结构的蓝图。它们告诉您数据应该如何组织。
message Recipe {
string dish_name = 1;
repeated string ingredients = 2;
double preparation_time_minutes = 3;
}
在这个例子中,我们创建了一个名为Recipe的消息,其中包含三个字段:dish_name表示菜名,ingredients表示配料列表(可以有多个),preparation_time_minutes表示制作这道菜所需的时间。每个字段都有一个唯一的编号(例如,1,2,3),用于组织。
2.字段类型
Protobuf支持各种字段类型,如字符串、整数、浮点数、枚举等等。您甚至可以嵌套消息以创建复杂的数据结构。这些字段类型确保数据结构良好,类型正确。
3.字段标签
消息中的字段可以具有标签,确定它们是required、optional还是repeated(用于列表):
- Required字段:这些字段必须始终出现在此类型的消息中。如果在序列化消息时缺少一个必需字段,将导致错误。
- Optional字段:它们可以包含在消息中,但不是必需的。如果在序列化消息时省略了可选字段,它将被视为具有默认值。
- Repeated字段:重复字段允许在单个字段中具有相同类型的多个值。它们用于数据的列表或数组。
4.枚举
枚举允许您定义一组命名的常量值。当您有一个字段具有预定义选项集,例如一周的日期或产品类别时,它非常有用。
enum DayOfWeek {
MONDAY = 1;
TUESDAY = 2;
// ...
}
5.注释
您可以在Protobuf定义中包含注释,以更好地解释您的消息和字段。注释可以以//开始,也可以包装在/* ... */中。
6.语法版本:规则和特性
Protobuf提供不同的语法版本,其中proto2和`proto3`是最常见的。这些版本定义了您可以在Protobuf定义中使用的规则和特性。
注意:建议gRPC API使用Protocol Buffers版本3(proto3)来定义API
7.导入其他文件:保持组织
对于更大的项目,您可以将Protobuf定义分成多个文件,并使用import语句将它们组合在一起。
序列化和反序列化
Protobuf的线路格式是二进制编码,因此处理起来更快。它使用一些巧妙的技巧来最小化用于表示消息的字节数。不需要了解二进制编码格式的知识来使用Protobuf。
为了真正理解Protocol Buffers(Protobuf)的威力,让我们通过一个示例,演示数据是如何序列化和编码,以及随后如何解码回来的。
考虑以下数据:
(我们使用前面定义的person.proto)
{
"name": "Ankit",
"id": 21,
"email": "username@gmail.com"
}
1.序列化和编码
Protobuf将此JSON数据转换为一个既高效又节省空间的二进制格式。在这种情况下,Protobuf编码如下:
0a 05 41 6e 6b 69 74 10 15 1a 12 75 73 65 72 6e 61 6d 65 40 67 6d 61 69 6c 2e 63 6f 6d
2.解码
现在,让我们颠倒这个过程,将这个Protobuf数据解码回其原始形式:
这个解码过程使Protobuf如此高效和强大。它确保数据保持一致和结构化,即使在编码和解码后,这使得它成为各种场景中数据传输的首选选择。
这只是一个简单的示例,如果您有兴趣,可以在Protocol Buffers网站上了解更多信息。
gRPC中的Protocol Buffers
Protocol Buffers(Protobuf)在gRPC中至关重要,为客户端和服务器之间提供了高效和一致的通信。以下是它们至关重要的原因:
- API契约定义:Protobuf为gRPC定义了消息结构,确保了高效和无错误的数据传输。
- 高效的序列化:Protobuf的二进制格式加速了数据序列化和反序列化,提高了gRPC的性能。
- 语言中立性:Protobuf的语言不可知性使其能够无缝集成到各种编程语言中。
- 高效性:Protobuf的二进制格式减少了网络使用,使数据传输更快。
- 互操作性:Protobuf充当通用翻译器,使gRPC服务能够在不同语言和平台之间轻松通信。
- 向后兼容性:Protobuf的版本支持允许API演进而不破坏现有客户端。
- 代码生成:Protobuf简化了消息结构代码生成,简化了开发流程。
- 性能:Protobuf的高效序列化和反序列化增强了gRPC服务的整体性能。
让我们包括一个使用Protocol Buffers(Protobuf)定义的gRPC服务的示例。假设我们正在构建一个带有用户身份验证的聊天应用程序。
这是我们服务的Protobuf定义:
syntax = "proto3";
message User {
string id = 1;
string username = 2;
}
message Message {
string id = 1;
string text = 2;
User sender = 3;
}
service ChatService {
rpc SendMessage(Message) returns (Message);
rpc GetMessages(User) returns (stream Message);
}
在这个示例中,我们定义了两种消息类型,User和Message,以及一个允许发送和接收消息的ChatService。使用Protobuf,这个服务定义清晰、简洁,并且可以轻松地生成成各种编程语言的代码。
总之,Protocol Buffers(Protobuf)已经彻底改变了数据序列化、传输和在不同系统中的理解方式。它们的效率、跨平台兼容性和结构化数据处理使它们成为现代应用程序的强大选择。
通过了解Protobuf的语法、核心概念以及它在gRPC中的作用,您可以充分利用它的功能。在探索Protobuf的世界时,请记住官方Protocol Buffers文档是深入了解这项技术的综合指南。