[关闭]
@Yano 2016-12-15T14:18:45.000000Z 字数 4467 阅读 2889

Google Protocol Buffers 数据交换协议

协议


protobuf 简介

protobuf是什么

protobuf(Protocol Buffers)是Google推出的一个结构化数据交换协议,用于传递自定义的消息格式,可用于同一台机器的进程间、不同设备进程间的数据传递。protobuf是一种语言无关、平台无关、高效、扩展性良好的语言,提供了一种将结构化数据进行序列化和反序列化的方法。

相对于XML,protobuf的体积更小、速度更快、使用更简单。我们仅需要定义一次数据结构,就可以很轻松地使用生成的代码读/写数据,而且这些数据结构是向后兼容的。

官方网站

https://developers.google.com/protocol-buffers/

protobuf的优劣

为什么不使用XML?

相对于XML来说,Protocol buffers在序列化结构化数据上,具有非常明显的优势:

如果要生成一个具有nameemailperson实例,XML做法如下:

  1. <person>
  2. <name>John Doe</name>
  3. <email>jdoe@example.com</email>
  4. </person>

使用protocol:

  1. # Textual representation of a protocol buffer.
  2. # This is *not* the binary format used on the wire.
  3. person {
  4. name: "John Doe"
  5. email: "jdoe@example.com"
  6. }

当此消息被编码为二进制格式时,长度大概是28字节,解析时间为100~200ns。在去除所有空格后,XML版本也至少为69字节,解析时间长达5000~10000ns。

同时,使用protocol buffer更简单:

  1. cout << "Name: " << person.name() << endl;
  2. cout << "E-mail: " << person.email() << endl;

使用XML的代码:

  1. cout << "Name: "
  2. << person.getElementsByTagName("name")->item(0)->innerText()
  3. << endl;
  4. cout << "E-mail: "
  5. << person.getElementsByTagName("email")->item(0)->innerText()
  6. << endl;

相对于JSON来说,protobuf也有明显的优势:

protobuf也有一些缺点,并不是适合所有场景。

如何使用protobuf

下面是通过Java使用protobuf的官方示例:https://developers.google.com/protocol-buffers/docs/javatutorial ,并在此基础上进行了简化。

定义.proto文件

定义需要序列化的数据结构,为message中的每一个变量设置名称和类型。下面

  1. package tutorial;
  2. option java_package = "yano";
  3. option java_outer_classname = "AddressBookProtos";
  4. message Person {
  5. required string name = 1;
  6. required int32 id = 2;
  7. optional string email = 3;
  8. enum PhoneType {
  9. MOBILE = 0;
  10. HOME = 1;
  11. WORK = 2;
  12. }
  13. message PhoneNumber {
  14. required string number = 1;
  15. optional PhoneType type = 2 [default = HOME];
  16. }
  17. repeated PhoneNumber phone = 4;
  18. }
  19. message AddressBook {
  20. repeated Person person = 1;
  21. }

定义字段时,我们使用了required、optional、repeated三个关键字。这些关键字表示对字段的约束,分别表示:

proto文件中的其它格式,在此不作介绍,详细内容可以参考官方文档。

编译protocol buffer

现在我们有了一个.proto文件,接下来就需要将生成class文件,我们可以通过这个class文件读写AddressBook的消息。

  1. proto编译器下载地址:https://developers.google.com/protocol-buffers/docs/downloads
  2. 运行编译器,指定proto路径、生成路径、.proto文件。
  1. protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

Protocol Buffer API

通过proto编译器,addressbook.proto生成了Java类AddressBookProtos.java。每个class都有自己的Builder,用以生成该类的实例。

  1. // required string name = 1;
  2. public boolean hasName();
  3. public String getName();
  4. // required int32 id = 2;
  5. public boolean hasId();
  6. public int getId();
  7. // optional string email = 3;
  8. public boolean hasEmail();
  9. public String getEmail();
  10. // repeated .tutorial.Person.PhoneNumber phone = 4;
  11. public List<PhoneNumber> getPhoneList();
  12. public int getPhoneCount();
  13. public PhoneNumber getPhone(int index);

Person.Builder 除了getters,还有setters方法:

  1. // required string name = 1;
  2. public boolean hasName();
  3. public java.lang.String getName();
  4. public Builder setName(String value);
  5. public Builder clearName();
  6. // required int32 id = 2;
  7. public boolean hasId();
  8. public int getId();
  9. public Builder setId(int value);
  10. public Builder clearId();
  11. // optional string email = 3;
  12. public boolean hasEmail();
  13. public String getEmail();
  14. public Builder setEmail(String value);
  15. public Builder clearEmail();
  16. // repeated .tutorial.Person.PhoneNumber phone = 4;
  17. public List<PhoneNumber> getPhoneList();
  18. public int getPhoneCount();
  19. public PhoneNumber getPhone(int index);
  20. public Builder setPhone(int index, PhoneNumber value);
  21. public Builder addPhone(PhoneNumber value);
  22. public Builder addAllPhone(Iterable<PhoneNumber> value);
  23. public Builder clearPhone();

创建一个 Person 实例:

  1. Person john =
  2. Person.newBuilder()
  3. .setId(1234)
  4. .setName("John Doe")
  5. .setEmail("jdoe@example.com")
  6. .addPhone(
  7. Person.PhoneNumber.newBuilder()
  8. .setNumber("555-4321")
  9. .setType(Person.PhoneType.HOME))
  10. .build();

标准消息方法

以下方法能够让我们检查和操作整个message:

解析和序列化

所有的protocol buffer类都有读写二进制的方法:

扩展protobuf

在扩展proto文件时,需要注意以下事项:

结束语

我的博客:http://www.jianshu.com/users/6835c29fc12a/latest_articles
我的知乎:https://www.zhihu.com/people/liu-jia-yu-58/activities

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注