RPC分布式网络通信框架(一)—— protobuf的使用

这篇具有很好参考价值的文章主要介绍了RPC分布式网络通信框架(一)—— protobuf的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、protobuf的好处

常见序列化和反序列化协议有XML、JSON、protobuf,相比于其他protobuf更有优势:
1、protobuf是二进制存储的,xml和json都是文本存储的。故protobuf占用带宽较低
2、protobuf不需要存储额外的信息。
json如何存储数据?键值对。例:Name:”zhang san”, pwd: “12345”。
protobuf存储数据的方式:“zhang san” “123456”(无额外信息)
3、protobuf跨平台语言支持。
可以直接在同构和异构系统中进行调用。异构系统指的是有的RPC进程是C++写的服务,有的RPC进程是Golang 或者Java 写的服务,但因为都是基于统一的protobuf 协议进行通信的,所以直接可以进行远程RPC通信。
4、protobuf序列化和反序列化效率高速度快且序列化后体积比XML和JSON都小很多,适合网络传输。
RPC分布式网络通信框架(一)—— protobuf的使用,rpc,分布式,c++
参考链接

二、如何创建proto

1、定义版本和声明,第三个为生成service所需要的声明(service服务类和rpc方法描述默认不生成)

syntax = "proto3";
package fixbug;
option cc_generic_services = true;

2、定义远端调用函数的input参数和return参数

message ResultCode
{
    int32 errcode = 1; 
    bytes errmsg = 2;
}

message LoginRequest
{
    bytes name = 1;
    bytes pwd = 2;
}

message LoginResponse
{
    ResultCode result = 1;
    bool sucess = 2;
}

注意,建议将string类型替换为bytes类型,因为bytes直接存二进制文件,效率更高一点,如果用string,最后还要将其转换为字节数据,而bytes则不需要。最后结果和上面相同

3、生成rpc方法的类型
在protobuf里面定义描述rpc方法的类型 – service

service UserServiceRpc
{
    rpc Login(LoginRequest) returns(LoginResponse);
}

三、编译生成的C++类

编译生成cpp和h文件
protoc test.proto --cpp_out=./

1、message具体生成的c++类如下
RPC分布式网络通信框架(一)—— protobuf的使用,rpc,分布式,c++

2、rpc方法的类型生成的类
test.proto代码

service UserServiceRpc
{
    rpc Login(LoginRequest) returns(LoginResponse);
}

生成类如下:
RPC分布式网络通信框架(一)—— protobuf的使用,rpc,分布式,c++

可以看出,一共生成两个类

UserServiceRpc

一个供callee–>rpc服务提供者使用。继承goole::protobuf::Service得到
class UserServiceRpc : public google::protobuf::Service

UserServiceRpc_Stub

一个供caller–>rpc服务的调用者使用。继承UserServiceRpc得到
class UserServiceRpc_Stub : public UserServiceRpc

成员函数很干净,一切的源头只需要一个RpcChannel类
RpcChannel类中只需要重写一个CallMethod方法,如下
RPC分布式网络通信框架(一)—— protobuf的使用,rpc,分布式,c++

四、序列化和反序列化

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

protobuf跨平台语言支持,序列化和反序列化效率高速度快,且序列化后体积比XML和JSON都小很多,适合网络传输。

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

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

序列化

1、定义生成的头文件
#include "test.pb.h"

2、函数调用方打包数据

LoginRequest reqA;
req.set_name("zhang san");
req.set_pwd("123456");

3、将打包好的LoginRequest reqA;数据交给protobuf进行序列化

std::string send_str;
// 进行序列化,框架干的事情
if (req.SerializeToString(&send_str))
{
	// 序列化成功后 再发送
    std::cout << send_str.c_str() << std::endl;
}

反序列化

此时数据被发送到被调用方,被调用方反序列化刚刚发送过来的send_str

LoginRequest reqB;
// 从send_str反序列化一个login请求对象
if (reqB.ParseFromString(send_str))  
{
	// 以下代码不属于框架内的代码
    std::cout << reqB.name() << std::endl;
    std::cout << reqB.pwd() << std::endl;
}

需要注意,所有不涉及抽象层,设计具体的业务的代码,都不属于RPC分布式网络通信框架的代码。

粘包问题解决

TCP是字节流协议,因此需自己处理拆包粘包问题,即自定义数据传输格式。
方案:
rpc服务调用者和rpc服务提供者发送或解析函数的输入数据时,需要共同参照一个proto数据包格式RpcHeader,如下所示:
RPC分布式网络通信框架(一)—— protobuf的使用,rpc,分布式,c++

syntax = "proto3";
package mprpc;

message RpcHeader
{
    bytes service_name = 1;
    bytes method_name = 2;
    uint32 args_size = 3;
}

rpc服务调用者和rpc服务提供者都需要遵循该格式组装数据或是解析数据。

调用者组包

首先调取服务名和方法名,之后序列化调用函数的输入,得到序列化后输入的长度。

将服务名,方法名,输入的长度按照预设定的protobuf message再次序列化得到rpc_header_str;
最后将rpc_header_str头部插入固定4字节的rpc_header_str.size(),尾部插入序列化后的调用函数输入,得到send_rpc_str。

const google::protobuf::ServiceDescriptor* sd = method->service();
std::string service_name = sd->name(); // service_name
std::string method_name = method->name(); // method_name

// 获取参数的序列化字符串长度 args_size
uint32_t args_size = 0;
std::string args_str;
if (request->SerializeToString(&args_str))
{
    args_size = args_str.size();
}
else
{
    controller->SetFailed("serialize request error!");
    return;
}

// 定义rpc的请求header
mprpc::RpcHeader rpcHeader;
rpcHeader.set_service_name(service_name);
rpcHeader.set_method_name(method_name);
rpcHeader.set_args_size(args_size);

uint32_t header_size = 0;
std::string rpc_header_str;
if (rpcHeader.SerializeToString(&rpc_header_str))
{
    header_size = rpc_header_str.size();
}
else
{
    controller->SetFailed("serialize rpc header error!");
    return;
}

// 组织待发送的rpc请求的字符串
std::string send_rpc_str;
send_rpc_str.insert(0, std::string((char*)&header_size, 4)); // header_size
send_rpc_str += rpc_header_str; // rpcheader
send_rpc_str += args_str; // args

// 打印调试信息
std::cout << "============================================" << std::endl;
std::cout << "header_size: " << header_size << std::endl; 
std::cout << "rpc_header_str: " << rpc_header_str << std::endl; 
std::cout << "service_name: " << service_name << std::endl; 
std::cout << "method_name: " << method_name << std::endl; 
std::cout << "args_str: " << args_str << std::endl; 
std::cout << "============================================" << std::endl;

提供者解包

根据头的长度,得到rpc_header_str的长度,使用substr将rpc_header_str从网络数据包中宅出来,并将数据头反序列化。
之后根据输入的长度反序列化输入args_str,成功拿到输入。文章来源地址https://www.toymoban.com/news/detail-573216.html

std::string recv_buf = buffer->retrieveAllAsString();

// 从字符流中读取前4个字节的内容
uint32_t header_size = 0;
recv_buf.copy((char*)&header_size, 4, 0);

// 根据 header_size 读取数据头的原始字符流,反序列化数据,得到rpc请求的详细信息
std::string rpc_header_str = recv_buf.substr(4, header_size);
mprpc::RpcHeader rpcHeader;
std::string service_name;
std::string method_name;
uint32_t args_size;
if (rpcHeader.ParseFromString(rpc_header_str))
{
    // 数据头反序列化成功
    service_name = rpcHeader.service_name();
    method_name = rpcHeader.method_name();
    args_size = rpcHeader.args_size();
}
else
{
    // 数据头反序列化失败
    std::cout << "rpc_header_str:" << rpc_header_str << " parse error!" << std::endl;
    return;
}

// 获取rpc方法参数的字符流数据
std::string args_str = recv_buf.substr(4 + header_size, args_size);

// 打印调试信息
std::cout << "============================================" << std::endl;
std::cout << "header_size: " << header_size << std::endl; 
std::cout << "rpc_header_str: " << rpc_header_str << std::endl; 
std::cout << "service_name: " << service_name << std::endl; 
std::cout << "method_name: " << method_name << std::endl; 
std::cout << "args_str: " << args_str << std::endl; 
std::cout << "============================================" << std::endl;

到了这里,关于RPC分布式网络通信框架(一)—— protobuf的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 分布式 RPC 框架HSF

    分布式 RPC 框架HSF

    HSF (High-speed Service Framework),高速服务框架,是在阿里巴巴内部广泛使用的分布式 RPC 服务框架。 HSF 作为阿里巴巴的基础中间件,联通不同的业务系统,解耦系统间的实现依赖。HSF 从分布式应用的层面,统一了服务的发布/调用方式,从而帮助用户可以方便、快速的开发分布式

    2024年02月16日
    浏览(10)
  • 分布式RPC框架Dubbo详解

    分布式RPC框架Dubbo详解

    目录   1.架构演进 1.1 单体架构 1.2  垂直架构 1.3 分布式架构 1.4 SOA架构 1.5 微服务架构 2.RPC框架 2.1 RPC基本概念介绍 2.1.1 RPC协议 2.1.2 RPC框架 2.1.3 RPC与HTTP、TCP/ UDP、Socket的区别 2.1.4 RPC的运行流程  2.1.5 为什么需要RPC 2.2 Dubbo  2.2.1 Dubbo 概述 2.2.2 Dubbo实战   架构演进如下图: 这

    2024年02月07日
    浏览(20)
  • 分布式系统消息通信技术:MOM与RPC

    分布式系统消息通信技术:MOM与RPC

    中间件(Middleware)是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台(包括开发平台和运行平台),但在这组中间件中必须要有一个通信中间件,即中间件+平台+通信,这

    2024年02月11日
    浏览(12)
  • 【项目实战】分布式计算和通信框架(AKKA)入门介绍

    【项目实战】分布式计算和通信框架(AKKA)入门介绍

    Akka是一个用于构建高并发、分布式、可容错、事件驱动的应用程序的工具包和运行时。它基于Actor模型,提供了一种高效的并发编程模型,可以轻松地编写出高并发、分布式、可容错的应用程序。Akka还提供了一些常用的组件,如路由、集群、持久化等,可以帮助开发人员更加

    2024年02月08日
    浏览(10)
  • 10 - 网络通信优化之通信协议:如何优化RPC网络通信?

    10 - 网络通信优化之通信协议:如何优化RPC网络通信?

    微服务框架中 SpringCloud 和 Dubbo 的使用最为广泛,行业内也一直存在着对两者的比较,很多技术人会为这两个框架哪个更好而争辩。 我记得我们部门在搭建微服务框架时,也在技术选型上纠结良久,还曾一度有过激烈的讨论。当前 SpringCloud 炙手可热,具备完整的微服务生态,

    2024年02月11日
    浏览(10)
  • 分布式【RPC 常见面试题】

    一、注册中心 策略:服务注册原理、注册中心结构、zookeeper的原理、几个注册中心的区别、分布式算法、分布式事务。 项目细节:服务注册、服务发现、服务注销、监听机制 介绍一下服务注册中心怎么做的? (1)服务发现: 服务注册/反注册:保存服务提供者和服务调用者

    2024年02月03日
    浏览(14)
  • 分布式理论CAP、BASE和RPC

    CAP原则是指当分布式系统遇到网络分区时,只能满足其中两个需求,一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)。在实际系统中,我们常常会选择在CA、CP或AP三者中做出取舍。 CA模型 CA模型要求分布式系统保持强一致性,即所有节点上的数据都

    2023年04月10日
    浏览(14)
  • 【DDD分布式系统学习笔记】RPC调用以及系统初步搭建

    modelVersion: 模型版本,指定POM模型的版本,目前使用的是Maven 4.0.0版本。 groupId: 项目的组织标识符,通常是组织的域名倒序。在这里是 cn.itedus.lottery。 artifactId: 项目的唯一标识符,通常是项目的名称。在这里是 Lottery。 packaging: 项目的打包方式,这里是 pom,表示这是一个聚合

    2024年01月18日
    浏览(14)
  • 分布式通信方式

      分布式通信是指在分布式系统中,不同节点之间进行消息传递和交互的方式。   以下是常见的分布式通信方式:   使用消息队列作为中间件,节点之间通过发送和接收消息来实现通信。消息队列提供了异步、解耦和可靠性的通信机制,常见的消息队列系统包括Rabb

    2024年02月15日
    浏览(10)
  • ROS:分布式通信

    ROS:分布式通信

    ROS是一个分布式计算环境。一个运行中的ROS系统可以包含分布在多台计算机上多个节点。根据系统的配置方式,任何节点可能随时需要与任何其他节点进行通信。 因此,ROS对网络配置有某些要求: 所有端口上的所有机器之间必须有完整的双向连接。 每台计算机必须通过所有

    2024年02月12日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包