Protobuf是谷歌发布的一套用于序列化结构数据的方法,全称Protocol Buffers。它包含了一套接口描述语言,用来描述一些数据结构,并使用配套工具生成代码,解析或序列化成数据流。其主要目的是希望能够比XML更轻量、快速,以此提高处理效率。
一般而言经常用到的两个部分包括Message和RPC,在开始之前,我们使用一个简单的示例来描述一下:
//message是Protobuf的数据结构,类似于C语言的Structure,其包含了一系列数据域 message HelloWorld{ required string name = 1; // message的数据域,相当于C语言中Structure的变量/Field optional int32 number = 2; repeated string message = 3; } message Response{ optional string response = 1; } rpc HelloWorldRpc (HelloWorld) returns (Response){}
如上所示定义了一个Protobuf消息,它包含了一个名叫HelloWorld的Message,而这个Message包含name、number和message三个字段每个字段前的修饰符(required / optional / repeated)则代表了数据的特性,字段后的数字代表他的TAG,这个TAG用于消息的序列化。
而同时,我们还定义了一个RPC,RPC全称为“远程过程调用”,简单的理解就是,发送一个消息给远端Server,然后远端Server执行一个逻辑,返回一个结果。这有点类似于你去餐厅吃饭,告诉厨子“我要吃土豆炒肉片”这个消息,然后厨子就执行“做菜”这个逻辑以后返回“土豆炒肉片”给你。
所以总的来说你可以认为Protobuf是“面向消息编程的RPC框架”。
在Protobuf中,message定义的基本类型包括int32/int64 (类似于C语言的long),uint32/uint64 , string,bytes,并且,你可以在message中嵌套一个message,就像以前写C语言结构体的时候嵌套结构体一样。类似于下面:
message Ticket{ required int32 id = 1; required string key = 2; } message Passenger{ required int32 id_card = 1; required Ticket ticket = 2; optional string identification = 3; }
很简单的结构,对不对,那么之前说过,required / optional / repeated 代表这个数据域的特征,他们分别是:
- required : 必要数据域,此数据域在此Message中必须赋值,否则会抛异常。
- optional : 与required相反,这标志这个数据域是可有可无的。
- repeated : 表示这个字段会出现多次,实际代码实现类似于数组。
需要注意的是:在Protobuf v3中,Google移除了这些限定字,从v3开始除非标明为repeated字段,否则所有字段默认为optional,不再有required这一限定
然后我们在C++里面为他赋值:
/** message Ticket{ required int32 id = 1; required string key = 2; } message Passenger{ required int32 id_card = 1; required Ticket ticket = 2; optional string identification = 3; } */ Passenger pPassenger; pPassenger.set_id_card(123456); // set_xxxx 用于设置字段的值,字段名在方法中全部是小写 Ticket* ppTicket = pPassenger.mutable_ticket(); //如果需要修改嵌套的message,使用mutable_fieldname 来获取指针然后修改 ppTicket->set_id(2333); ppTicket->set_key("This is a key"); // get value string szTicketKey = ppTicket->key(); //get时直接使用其名称 //或者你也可以这样 szTicketKey = pPassenger.ticket().key(); //
当然你可能会注意到在这里我并没有去设置一个optional字段的值,那么对于一个optional字段,如果我没有设置值的话,应该如何处理呢?
答案是:你可以使用 message.has_xxxx() 来判断是否设置了这个字段。
当然,对于一个optional的字段,其在初始化时会有一个默认值,这个值在不同的类型下表现为:
string : 空字符串
数字类型(int/double/float等): 0
布尔类型(boolean) : false
Message : 空消息体
当一个optional字段没有被设置,而你调用了getter时,就会返回上面对应的默认值。