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


    01 序列化协议对比

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

    image-20220814235216125

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

    XMLJSONPB
    保存方式文本文本二进制
    可读性较好较好不可读
    解析效率一般
    语言支持所有语言所有语言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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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=./
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    03 Protobuf实现原理

    img

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

    04 粘包问题解决

    image-20220815120520672

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

  • 相关阅读:
    SpringBoot
    Golang:gocron定时任务管理系统的安装与使用
    linux高性能服务器
    纯代码方式杀死指定进程名的进程(Linux&Windows)
    对OceanBase进行 sysbench 压测前,如何用 obdiag巡检
    Elasticsearch实战(五)---高级搜索 Match/Match_phrase/Term/Must/should 组合使用
    Vue系列之入门篇
    抖店token的生成和刷新的实际开发笔记
    (CPU/GPU)粒子继承贴图颜色发射
    本地环境运行Llama 3大型模型:可行性与实践指南
  • 原文地址:https://blog.csdn.net/qq_41945053/article/details/127721883