protobuf概述

什么是protobuf

protobuf全称protocol buffers, 它是google为了序列化结构体而开发的一种跨语言、跨平台、可拓展的一种协议,类似于xml,但更小、更快、更简单。使用protobuf,只要定义了结构体,就可以使用生成的代码,去生成、解析结构体。

工作原理

在protobuf中,我们使用.proto文件来定义结构体。在.proto文件中,包含一系列的键值对,来记录数据。如,下面就是一个.proto文件,用来描述一个人的信息:

    message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}

repeated PhoneNumber phone = 4;
}

如上面的代码,每个message类型都有一个或者多个不重复的数据块,每个数据块都由键值对组成,其中的值可以是数字(integer或者float),boolean, strings, raw bytes,或者protobuf协议中其他的message类型(类似于对象,对象中可以包含对象)。数据块可以声明为可选、必选或者可重复的。更多关于.proto的语法,参考https://developers.google.cn/protocol-buffers/docs/proto

一旦定义完.proto文件,就可以使用proto编译器编译出指定语言的源码,包括java, python, c++。生成的代码中,会声明一个Person类, 通过Person类,我们可以很方便的序列化、反序列化Person数据:

    Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("jdoe@example.com");
fstream output("myfile", ios::out | ios::binary)
;
person.SerializeToOstream(&output);

上面的代码,把Person对象序列化到文件中了,下面我们可以再把Persson对象解析出来:

    fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

我们可以直接想message中增加新字段,而不用担心兼容性;当解析老二进制数据时,会自动忽略新字段。

为什么不使用xml

和xml相比,protobuf有很多优点:

  • 更简单
  • 比xml,小3-10倍
  • 比xml,快20-100倍
  • 更不容易产生歧义
  • 使用生成的源码,操作类,对开发者更友好

如下面的例子,如果一个personnameemail属性,我们需要定义xml如下:

<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>

而使用protobuf,如下(protobuf的文本格式)

    #注意真正的protobuf数据是二进制数据流
person {
name: "John Doe"
email: "jdoe@example.com"
}

当上面的数据被编码成protobuf二进制数据时,只有28字节;反序列化,解析成对象需要100-200纳秒。而xml数据,需要69字节;反序列化需要5000-10000纳秒。

并且操作protobuf也很简单:

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

如果是xml,我们需要:

    cout << "Name: "
<< person.getElementsByTagName("name")->item(0)->innerText()
<< endl;
cout << "E-mail: "
<< person.getElementsByTagName("email")->item(0)->innerText()
<< endl;

当然,protobuf也不都是比xml强。protobuf数据是以二进制数据的方式存储的,人类难以阅读。而xml是自描述的,我们可以直接理解其中的含义;除非我们拿到.proto文件,否则是很难知道protobuf数据的含义的。

友荐云推荐

发表评论

电子邮件地址不会被公开。 必填项已用*标注

(Spamcheck Enabled)