Protobuf的RPC序列化和调用原理

前面也有一篇文章,当时直接深入到底层的代码了,反而对于这个调用过程不是很熟悉了,所以这里进行讲讲。(有兴趣可以看一下:基于Protobuf的RPC_proto rpc-CSDN博客

对于分布式系统 ,我们需要协调不同系统间的通信机制,因此就有了RPC和Restful api的两个主流的形式。Restful 主要是基于http协议作为一个通信的载体,因此其更多的是面向公共的接口,提供外部服务用的。而RPC则是自己定义的一个方法,然后设计方法参数和返回值的,restful基于http的方法进行访问资源的。

有了restful,为什么要考虑用rpc呢?我们回到使用场景,在分布式内部调用服务的时候,肯定是要保证速度的,所以速度是第一位 的。因此就有了早期的将rpc的消息序列化成二进制的格式进行通过网络进行传输。但是,随着安全性的提升,必须要保证消息的安全性,所以就有了基于http2进行数据传输的grpc,利用了http2的分帧,二进制,头部压缩,流控制的方式进行优化。

前言讲完了,我们这里讲讲最基础的rpc,基于Protobuf的rpc。我们还是直接使用以前的例子,做简单的分析。

复制代码
message ResultCode
{
    int32 errcode = 1; 
    bytes errmsg = 2;
}
message LoginRequest
{
    bytes name = 1;
    bytes pwd = 2;
}
message LoginResponse
{
    ResultCode result = 1;
    bool sucess = 2;
}
message RegisterRequest
{
    uint32 id = 1;
    bytes name = 2;
    bytes pwd = 3;
}
message RegisterResponse
{
    ResultCode result = 1;
    bool sucess = 2;
}
service UserServiceRpc
{
    rpc Login(LoginRequest) returns(LoginResponse);
    rpc Register(RegisterRequest) returns(RegisterResponse);
}

在网络中数据传输,一般有基于像http的/r/n作为一个消息进行传输。而基于二进制的数据怎么传输呢?我们可以通过,message_length(4字节)+message_content(message表示的长度)。message怎么设计呢?因为message内部也有不同的属性,比如ResultCOde:可以设计成一个字节的类型表示int,四个字节表示真实的errcode的真实内容。所以一个ResultCode可以设计成message_length(4字节)+message_content((1字节表示类型+4字节的类型内容)+(1字节类型+1字节类型)....)最后都生成的是二进制的数据。这里类型可能也会进行压缩,比如使用4 bit进行区分等等。

我们这里拓展,讲讲上面的代码,我们看到message是消息的类型。Service则是定义内部分布式调用的服务,rpc Login(LoginRequest) returns(LoginResponse);则是定义了一个方法。所以,这里服务器A提供A服务,服务器B提供B服务。A和B服务可能有很多方法,我们怎么做区分呢?这里就是有一个或者多个的数组,Protobuf里称为描述符的东西,我们直接通过一个Service找到其方法的描述符,同时一个方法的描述符内部也有一个服务的描述符,二者的关系是(一个Service,多个method)。

这里,我们讲一下整体的过程:

1.服务的发起者

复制代码
调用方:Stub
//1MprpeChannel是底层序列化request的地方,以及发送数据的地方
fxbug:UsereveStustuwhnnelO)
//2.rpe方法的请求参数
fixbug:LoginRequest request
request.set_name("zhang san");
requestset_pwd("123456");
//3.rpe方法的响应
fixbug:LoginResponse response,
//4.发起rpc方法的调用同步的rpc调用过程MprpcChannel:callmethod
a
// 4.1 定义rpe的请求header
mprpc:RpcHeader rpcHeader;,
pcHeader.setservicnameservicname);
rpcHeader.setmethodname(methodname);
rpHeadersetarg_size(args_size);
//4.2 序列化rpcheader
uint32 theader size =0;
tstringpheaderst
if(pHeaderSerializeToString(&pheaderst)
{
header_size=pcheader_st.size0:
1
else
{
controller->SetFailed("serializepheadererror!"):
return;
}
4.3序列化所有消息
//组织待发送的rpc请求的字符串
std:string send_rpc_st,
isis
send_xpc_st+=rpc_headet_st://rpcheader
send_xpc_st+=arg_st;//arg
5. 反序列化Response
response->ParseFromArray
  1. 服务的提供者

    1. 服务的发布注册
      RpcProvider::NotifyService(google::protobuf::Service *service)
    2. 请求的解析
      mprpc::RpcHeader rpcHeader;
      std::string service_name;
      std::string method_name;
      uint32_t args_size;
      rpcHeader.ParseFromString(rpc_header_str)
      通过序列化得到的数据,得到对应的方法名,最后找到对应的方法
      3.执行request的回调,返回response
      google::protobuf::Closure done = google::protobuf::NewCallback<RpcProvider,
      const muduo::net::TcpConnectionPtr&,
      google::protobuf::Message
      >(this,&RpcProvider::SendRpcResponse, conn, response);
      调用服务的回调
      service->CallMethod(method, nullptr, request, response, done);
相关推荐
茯苓gao1 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
万联WANFLOW1 小时前
SD-WAN 控制平面高可用怎么做?SDWAN 控制器挂了,全网会发生什么
运维·网络·分布式·架构
酱学编程2 小时前
【从零到一实现一个 AI Agent 框架 · 第四篇】04. 任务规划:拆解复杂目标 -
服务器·网络·数据库·人工智能
风行南方3 小时前
密码学之分组密码
网络·密码学
艾莉丝努力练剑4 小时前
OpenCode AI 编程:Ubuntu 24.04 环境安装与使用指南
linux·服务器·网络·人工智能·tcp/ip·ubuntu
谷雪_6585 小时前
Linux 网络命名空间:从内核原理到企业级容器网络架构全景实战
linux·网络·架构
liulilittle5 小时前
KCC 拥塞控制算法缺陷修复记录(26/07/05)
网络·tcp/ip·计算机网络·c·信息与通信·tcp·通信
Tian_Hang5 小时前
Eclipse Ditto 的权限策略
java·服务器·前端·网络·ide·ubuntu·eclipse
ai生成式引擎优化技术5 小时前
从知识连接到智能组织:WSaiOS认知网络的理论框架与系统设计摘要
网络·python·plotly·django·pygame
冰暮流星6 小时前
flask之http请求方法
网络·网络协议·http