1、什么是protocol buffers
2、protocol buffers的工作流程
3、protobuffer和xml、json的区别
protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与json、xml相比,protocol buffers序列化后的码流更小、速度更快、操作更简单。你只需要将要被序列化的数据结构定义一次(使用.proto文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具protoc)轻松的使用不同的数据流完成对这些结构数据的读写操作,即使你使用不同的语言(protobuf的跨语言支持特性)。你甚至可以更新你的数据结构的定义(就是更新.proto文件内容)而不会破坏依“老”格式编译出来的程序。
安装方式如下:
首先从GitHub下载对应版本的protobuffer,然后解压,然后:
$ cd protobuf-
$ ./configure
$ make
$ make check
$ make install
(1)首先,你需要通过在.proto文件中定义protocol buffer的message类型来指定你想要序列化的数据结构:每一个protocol buffer message是一个逻辑上的信息记录,它包含一系列的键值对。如下展示一个最基本的.ptoto文件的例子,它定义了一个包含Person信息的message::
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;
}
(2)message数据格式中需要知道的:
required: 必须赋值,不能为空,否则该条message会被认为是“uninitialized”。build一个“uninitialized” message会抛出一个RuntimeException异常,解析一条“uninitialized” message会抛出一条IOException异常。除此之外,“required”字段跟“optional”字段并无差别。
optional:字段可以赋值,也可以不赋值。假如没有赋值的话,会被赋上默认值。
repeated: 该字段可以重复任意次数,包括0次。重复数据的顺序将会保存在protocol buffer中,将这个字段想象成一个可以自动设置size的数组就可以了。
protobuffer支持的数据类型如下:
.proto Type | Notes | C++ Type | Java Type | Python Type[2] |
---|---|---|---|---|
double | double | double | float | |
float | float | float | float | |
int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint32替代 | int32 | int | int |
int64 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 | int64 | long | int/long[3] |
uint32 | 使用变长编码 | uint32 | int[1] | int/long[3] |
uint64 | 使用变长编码 | uint64 | long[1] | int/long[3] |
sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int32 | int | int |
sint64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效。 | int64 | long | int/long[3] |
fixed32 | 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 | int[1] | int |
fixed64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 | long[1] | int/long[3] |
sfixed32 | 总是4字节 | int32 | int | int |
sfixed64 | 总是8字节 | int64 | long | int/long[3] |
bool | bool | boolean | boolean | |
string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 | string | String | str/unicode[4] |
bytes | 可能包含任意顺序的字节数据。 | string | ByteString | str |
(3)一旦定义了你的message,你就可以根据你所使用的语言(如JAVA、C++、Python等)使用protocol buffer提供的编译工具protoc来编译.proto文件生成数据访问类。这些类为每一个字段都提供了简单的访问器(比如name()和set_name()),同时还提供了将整个结构化数据序列化为原始字节数据以及从原始字节数据反序列化为结构化数据的方法(C++中称之为函数)。例如,如果你使用的语言是C++,运行编译器编译一个message的实例如下:
//xxx.proto
message Order
{
required uint64 uid = 1;
required float cost = 2;
optional string tag = 3;
}
//xxx.pb.h
class Order : public ::google::protobuf::Message {
public:
...
// accessors -------------------------------------------------------
// required uint64 uid = 1;
inline bool has_uid() const;
inline void clear_uid();
static const int kUidFieldNumber = 1;
inline ::google::protobuf::uint64 uid() const;
inline void set_uid(::google::protobuf::uint64 value);
// required float cost = 2;
inline bool has_cost() const;
inline void clear_cost();
static const int kCostFieldNumber = 2;
inline float cost() const;
inline void set_cost(float value);
// optional string tag = 3;
inline bool has_tag() const;
inline void clear_tag();
static const int kTagFieldNumber = 3;
inline const ::std::string& tag() const;
inline void set_tag(const ::std::string& value);
inline void set_tag(const char* value);
inline void set_tag(const char* value, size_t size);
inline ::std::string* mutable_tag();
inline ::std::string* release_tag();
inline void set_allocated_tag(::std::string* tag);
// @@protoc_insertion_point(class_scope:Order)
private:
inline void set_has_uid();
inline void clear_has_uid();
inline void set_has_cost();
inline void clear_has_cost();
inline void set_has_tag();
inline void clear_has_tag();
::google::protobuf::uint32 _has_bits_[1];
::google::protobuf::uint64 uid_;
::std::string* tag_;
float cost_;
};
对于每一个message的data member,protobuf会自动生成相关的处理函数,对于每一个字段主要的处理函数有:
has_uid(), clear_uid(), uid(), set_uid(),它们分别用于判断该字段是否被设置,清除该字段设置记录,
获得该字段,设置该字段。
(4)针对上述例子中的person类,之后你可能会写下如下类似的代码(序列化):
Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("[email protected]");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);
之后,你可以将你的message读回(译注:反序列化):
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
(1)protobuffer和xml
相对于XML,protocol buffers在序列化结构数据时拥有许多先进的特性:
1)更简单
2)序列化后字节占用空间比XML少3-10倍
3)序列化的时间效率比XML快20-100倍
4)具有更少的歧义性
5)自动生成数据访问类方便应用程序的使用
事物总有两面性,和XML相比protocol buffers并不总是更好的选择,例如:
1)protocol buffers并不适合用来描述一个基于文本的标记型文档(比如HTML),因为你无法轻易的交错文本的结构。
2)另外,XML具有很好的可读性和可编辑性;而protocol buffers,至少在它们的原生形式上并不具备这个特点。
3)XML同时也是可扩展、自描述的。而一个protocol buffer只有在具有message 定义(在.proto文件中定义)时才会有意义。
(2)protobuffer和json
1)JSON因为有一定的格式,并且是以字符存在的,在数据量上还有可以压缩的空间。protobuf是二进制的、结构化的,所以比json的数据量更小,也更对象化 ;
2)protobuf不是像json直接明文的,这个是定义对象结构,然后由protbuf库去把对象自动转换成二进制,用的时候再自动反解过来的。传输对我们是透明的!我们只管传输的对象就可以了。
总的来说,前端和后端交互会采用json,而后端各个模块的交互,你可以随便选择;对于HTTP协议的交互,用的比较多的是json,而 tcp协议,用的比较多的是protobuffer。
参考:https://www.cnblogs.com/chenyangyao/p/5422044.html