轻量级RPC分布式网络通信框架设计——序列化协议Protobuf

01 序列化协议对比

  • 序列化:对象转为字节序列称为对象的序列化
  • 反序列化:字节序列转为对象称为对象的反序列化

轻量级RPC分布式网络通信框架设计——序列化协议Protobuf_第1张图片

常见序列化和反序列化协议有XML、JSON、PB,相比于其他PB更有优势:跨平台语言支持,序列化和反序列化效率高速度快,且序列化后体积比XML和JSON都小很多,适合网络传输。

XML JSON PB
保存方式 文本 文本 二进制
可读性 较好 较好 不可读
解析效率 一般
语言支持 所有语言 所有语言 C++/Java/Python及第三方支持
适用范围 文件存储、数据交互 文件存储、数据交互 文件存储、数据交互

注意:序列化和反序列化可能对系统的消耗较大,因此原则是:远程调用函数传入参数和返回值对象要尽量简单,具体来说应避免:

远程调用函数传入参数和返回值对象体积较大,如传入参数是List或Map,序列化后字节长度较长,对网络负担较大
远程调用函数传入参数和返回值对象有复杂关系,传入参数和返回值对象有复杂的嵌套、包含、聚合关系等,性能开销大
远程调用函数传入参数和返回值对象继承关系复杂,性能开销大

02 源码解析

  • 比其他序列化语言 更小 更快 更简单

  • 每定义一个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=./

03 Protobuf实现原理

轻量级RPC分布式网络通信框架设计——序列化协议Protobuf_第2张图片

上面说了protobuf的message中有很多字段,每个字段的格式为:
修饰符 字段类型 字段名 = 域号;
在序列化时,protobuf按照TLV的格式序列化每一个字段,T即Tag,也叫Key;V是该字段对应的值value;L是Value的长度,如果一个字段是整形,这个L部分会省略。
序列化后的Value是按原样保存到字符串或者文件中,Key按照一定的转换条件保存起来,序列化后的结果就是 KeyValueKeyValue…。Key的序列化格式是按照message中字段后面的域号与字段类型来转换。

04 粘包问题解决

轻量级RPC分布式网络通信框架设计——序列化协议Protobuf_第3张图片

定义protobuf类型的结构体消息RpcHeader,包含服务对象、函数方法、函数参数,因为参数可变,参数长不定,因此不能和RpcHeader一起定义,否则多少函数就有多少RpcHeader,因此需为每个函数定义不同protobuf结构体消息,然后对该结构体消息序列化(字符串形式存储),就得到两个序列化后的二进制字符串,拼接起来就是要发送的消息,同时消息前需记录序列化后的RpcHeader数据的长度,这样才能分开RpcHeader和函数参数的二进制数据,从而反序列化得到函数参数
序列化后的二进制字符串,拼接起来就是要发送的消息,同时消息前需记录序列化后的RpcHeader数据的长度,这样才能分开RpcHeader和函数参数的二进制数据,从而反序列化得到函数参数

你可能感兴趣的:(#,后端分布式系统架构设计,rpc,分布式,网络)