常见序列化和反序列化协议有XML、JSON、PB,相比于其他PB更有优势:跨平台语言支持,序列化和反序列化效率高速度快,且序列化后体积比XML和JSON都小很多,适合网络传输。
XML | JSON | PB | |
---|---|---|---|
保存方式 | 文本 | 文本 | 二进制 |
可读性 | 较好 | 较好 | 不可读 |
解析效率 | 慢 | 一般 | 快 |
语言支持 | 所有语言 | 所有语言 | C++/Java/Python及第三方支持 |
适用范围 | 文件存储、数据交互 | 文件存储、数据交互 | 文件存储、数据交互 |
注意:序列化和反序列化可能对系统的消耗较大,因此原则是:远程调用函数传入参数和返回值对象要尽量简单,具体来说应避免:
远程调用函数传入参数和返回值对象体积较大,如传入参数是List或Map,序列化后字节长度较长,对网络负担较大
远程调用函数传入参数和返回值对象有复杂关系,传入参数和返回值对象有复杂的嵌套、包含、聚合关系等,性能开销大
远程调用函数传入参数和返回值对象继承关系复杂,性能开销大
比其他序列化语言 更小 更快 更简单
每定义一个message 都会生成一个类 每一个message里面的变量 bytes int32 bool 都会生成对应的方法 set_name() set_pwd()
使用版本proto3
编译结束后,会生成一个.h和一个.cc文件
src/rpcheader.proto
syntax = "proto3"
package mprpc;
message RpcHeader {
bytes service_name = 1;
bytes method_name = 2;
uint32 args_size = 3;
}
example/user.proto
syntax = "proto3"
package fixbug;
option cc_generic_services = true;
message ResultCode {
int32 errcode = 1;
bytes errmsg = 2;
}
message LoginRequest {
bytes name = 1;
bytes pwd = 2;
}
message LoginResponse {
ResultCode result = 1;
bool success = 2;
}
service UserServiceRpc {
rpc Login(LoginRequest) returns(LoginResponse);
}
//编译指令 protoc user.proto -cpp_out=./
上面说了protobuf的message中有很多字段,每个字段的格式为:
修饰符 字段类型 字段名 = 域号;
在序列化时,protobuf按照TLV的格式序列化每一个字段,T即Tag,也叫Key;V是该字段对应的值value;L是Value的长度,如果一个字段是整形,这个L部分会省略。
序列化后的Value是按原样保存到字符串或者文件中,Key按照一定的转换条件保存起来,序列化后的结果就是 KeyValueKeyValue…。Key的序列化格式是按照message中字段后面的域号与字段类型来转换。
定义protobuf类型的结构体消息RpcHeader,包含服务对象、函数方法、函数参数,因为参数可变,参数长不定,因此不能和RpcHeader一起定义,否则多少函数就有多少RpcHeader,因此需为每个函数定义不同protobuf结构体消息,然后对该结构体消息序列化(字符串形式存储),就得到两个序列化后的二进制字符串,拼接起来就是要发送的消息,同时消息前需记录序列化后的RpcHeader数据的长度,这样才能分开RpcHeader和函数参数的二进制数据,从而反序列化得到函数参数
序列化后的二进制字符串,拼接起来就是要发送的消息,同时消息前需记录序列化后的RpcHeader数据的长度,这样才能分开RpcHeader和函数参数的二进制数据,从而反序列化得到函数参数