Protobuf简单入门

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时,就会返回上面对应的默认值。