C++下Protobuf学习

C++下Protobuf简单学习

Protobuf(Protocol Buffers)协议是一种由 Google 开发的高效的跨语言的平台无关数据序列化协议,提供二进制序列化格式和相关的技术,它用于高效地序列化和反序列化结构化数据,通常用于网络通信、数据存储等场景。

Protobuf 在许多领域都得到了广泛应用,特别是在分布式系统、RPC(Remote Procedure Call)框架和数据存储中,它提供了一种高效、简洁和可扩展的方式来序列化和交换数据,Protobuf 的主要优点包括:

  • 高效性:Protobuf 序列化后的二进制数据通常比其他序列化格式(比如超级常用的JSON)更小,并且序列化和反序列化的速度更快,这对于性能敏感的应用非常有益。
  • 简洁性:Protobuf 使用一种定义消息格式的语法,它允许定义字段类型、顺序和规则(消息结构更加清晰和简洁)
  • 版本兼容性:Protobuf 支持向前和向后兼容的版本控制,使得在消息格式发生变化时可以更容易地处理不同版本的通信。
  • 语言无关性:Protobuf 定义的消息格式可以在多种编程语言中使用,这有助于跨语言的通信和数据交换(截至本文发布目前官方支持的有C++/C#/Dart/Go/Java/Kotlin/python)
  • 自动生成代码:Protobuf 通常与相应的工具一起使用,可以自动生成代码,包括序列化/反序列化代码和相关的类(减少了手动编写代码的工作量,提高效率)

消息定义

Protocol Buffer 消息message和服务service由程序员编写的 .proto 文件描述。下面显示了一个示例 消息

syntax = "proto2";		// 指定正在使用proto2语法message Person {optional string name = 1;optional int32 id = 2;optional string email = 3;
}

然后执行下列命令进行编译:

protoc --cpp_out=. person.proto

protoc 编译器对 .proto 文件进行处理,会生成两个文件:person.pb.hperson.pb.cc,以操作相应的 protocol buffer。其中.proto文件中的每一个消息有一个对应的类

.proto文件中的类型和各个语言中的类型匹配:

在这里插入图片描述

服务定义

如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer 编译器将会根据所选择的不同语言生成服务接口代码及存根。

例如,想要定义一个RPC服务并具有一个Search方法,该方法能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:

syntax = "proto3";message SearchRequest {string query = 1;
}message SearchResponse {string result = 1;
}service SearchService {//rpc 服务的函数名 (传入参数)返回(返回参数)rpc Search (SearchRequest) returns (SearchResponse);
}

对该.proto文件编译,会得到一个.pb.h和一个.pb.cc文件,包含SearchServiceSearchService_stub

SearchService类 —— 所定义的一个服务类

SearchService类继承于google::protobuf::Service类,是一个服务器端实现RPC服务的类****(因此这个过程就是程序员在.proto中定义了服务、服务方法以及服务方法需要传入的参数、返回的参数,然后通过解析将其转换成具体的语言代码形成一个服务类,类中定义了方法和调用这个方法的接口),源码大致如下:

class SearchSerive_Stub;class SearchService : public ::google::protobuf::Service {public:SearchService();virtual ~SearchService();SearchService(const SearchService&) = delete;SearchService& operator=(const SearchService&) = delete;typedef SearchService_Stub Stub;static const ::google::protobuf::ServiceDescriptor* descriptor();virtual const ::google::protobuf::ServiceDescriptor* GetDescriptor() const;virtual void Search(::google::protobuf::RpcController* controller,const ::search::SearchRequest* request,::search::SearchResponse* response,::google::protobuf::Closure* done);// implements Service ----------------------------------------------const ::google::protobuf::Message& GetRequestPrototype(const ::google::protobuf::MethodDescriptor* method) const override;const ::google::protobuf::Message& GetResponsePrototype(const ::google::protobuf::MethodDescriptor* method) const override;void CallMethod(const ::google::protobuf::MethodDescriptor* method,::google::protobuf::RpcController* controller,const ::google::protobuf::Message* request,::google::protobuf::Message* response,::google::protobuf::Closure* done) override;const ::google::protobuf::ServiceDescriptor* service_descriptor() const override;void Shutdown() override;private:GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SearchService);
};

下面是对这个类的详细说明:

  • 内部类别名Stub:利用typedef定义了一个SearchService_Stub类的别名stub,用于指向服务的客户端存根(Stub)。
  • 静态方法
    • descriptor():返回当前服务的描述符。
    • GetDescriptor():返回当前服务的描述符。
  • 虚方法声明
    • Search()虚函数:声明该方法接受一个 SearchRequest 请求,处理后将结果存入 SearchResponse 中,并在完成时调用 done 闭包。具体实现由派生类定义,此时生成的这个类主要用来作为基类。
  • Service 接口实现
    • GetRequestPrototype()GetResponsePrototype():根据给定的方法描述符,返回请求和响应消息的原型。
    • CallMethod():根据参数输入中的方法描述符调用本服务中相应的 RPC 方法。
    • service_descriptor():返回服务的描述符。
    • Shutdown():实现服务关闭时的逻辑。

SearchService_stub类 —— 客户端存根

SearchService_stub 类继承自SearchService类,客户端使用的存根(Stub),用于通过网络调用远程服务器上定义的 RPC 方法。

class SearchService_stub : public SearchService {public:SearchService_stub(::google::protobuf::RpcChannel* channel);SearchService_stub(::google::protobuf::RpcChannel* channel,::google::protobuf::Service::ChannelOwnership ownership);~SearchService_stub();inline ::google::protobuf::RpcChannel* channel() { return channel_; }// implements kvServerRpc ------------------------------------------void Search(::google::protobuf::RpcController* controller,const ::search::SearchRequest* request,::search::SearchResponse* response,::google::protobuf::Closure* done);private:::google::protobuf::RpcChannel* channel_;bool owns_channel_;GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(kvServerRpc_Stub);
};

显然可以看出,SearchService_stub 在构造的时候主要需要传入一个google::protobuf::RpcChannel类型的指针。RpcChannel类负责管理客户端与服务器之间的网络通信,抽象了底层的网络细节,包括连接建立、数据传输、错误处理等。通过 RpcChannel,客户端可以与服务器建立连接并保持通信状态。

于是,客户端可以实例化 SearchService_stub 类,是存根可以通过传入的 RpcChannel对象与所连接的服务器进行通信。然后客户使用存根对象调用服务器上定义的 RPC 方法,例如 Search 方法。

// 示例:准备请求和响应对象
search::SearchRequest request;
search::SearchResponse response;// 设置请求内容,例如填充 request 对象
...// 发起远程调用
stub.Search(nullptr, &request, &response, nullptr);

CallerMethod在其中发挥的作用?

这里涉及到了两个类型的CallerMethod方法:RpcChannel::CallerMethodService::CallerMethod.

在实际使用中,当客户端通过存根对象调用远程服务器上的 RPC 方法时,最终会执行到 CallMethod 方法。继续以上面的例子,方法内部**,它会通过 **RpcChannel 对象 CallMethod 方法**来执行实际的 RPC 调用过程。

void SearchService_stub::Search(::google::protobuf::RpcController* controller,const ::FooRequest* request,::FooResponse* response,::google::protobuf::Closure* done) {channel_->CallMethod(descriptor()->method(0),controller, request, response, done);
}

具体来说,这个方法调用之后会经过以下几件事情:

  1. 创建 RpcController 对象:如果传入的 controller 参数为 nullptr,则会创建一个默认的 RpcController 对象。
  2. 调用 CallMethod 方法SearchService::Stub 类内部的 Search 方法会调用 RpcChannel 对象的 CallMethod 方法,同时传递以下参数:
    • method 参数:指定要调用的 RPC 方法的描述符。
    • controller 参数:负责控制 RPC 调用过程,例如处理超时、错误处理等。
    • request 参数:包含了客户端发送的请求消息。
    • response 参数:用于存储服务器端处理后的响应消息。
    • done 参数:在 RPC 调用完成时调用的回调函数对象。
  3. 执行 RPC 调用(在RpcChannel::CallerMethod中定义):在 RpcChannel 对象的 CallMethod方法内部,根据 method 参数确定要调用的具体 RPC 服务名称和方法名称(例如 Search),然后构建请求头,和对应的请求消息一起发送给服务器。
  4. 处理响应(在服务端中定义):服务器端接收到消息后,根据请求头中的服务名称和方法名称,调用服务对象的CallMethod方法来处理请求,并且将生成的响应消息存储在响应消息response 参数中,并在完成时调用 done 对象通知调用方。

参考文献

上述内容多摘抄自以下博客,如有侵权,请联系删除:

保姆级】Protobuf详解及入门指南-CSDN博客

概览 | 协议缓冲区文档 - ProtoBuf 中文

Protobuf 完整解析 - 公司最常用的数据交互协议 - hongxinerke - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/41936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DDR3(三)

目录 1 预取1.1 什么是预取1.2 预取有哪些好处1.3 结构框图1.4 总结 2 突发2.1 什么是突发2.2 突发与预取 本文讲解DDR中常见的两个术语:预取和突发,对这两个概念理解的关键在于地址线的低位是否参与译码,具体内容请继续往下看。 1 预取 1.1…

JDBC【封装工具类、SQL注入问题】

day54 JDBC 封装工具类01 创建配置文件 DBConfig.properties driverNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/qnz01?characterEncodingutf8&serverTimezoneUTC usernameroot passwordroot新建配置文件,不用写后缀名 创建工具类 将变…

C++笔试强训2

文章目录 一、选择题二、编程题 一、选择题 和笔试强训1的知识点考的一样,因为输出的是double类型所以后缀为f,m.n对其30个字符所以m是30,精度是4所以n是4,不加符号默认是右对齐,左对齐的话前面加-号,所以答案是-30.4f…

推荐Bulk Image Downloader插件下载网页中图片链接很好用

推荐:Bulk Image Downloader chome浏览器插件下载图片链接,很好用。 有个网页,上面放了数千的gif的电路图,手工下载会累瘫了不可。想找一个工具分析它的静态链接并下载,找了很多推荐的下载工具,都是不能分…

vue2 data内对象引用另一个data对象无法使用this的解决办法

背景:data内有一复杂对象,并且内部一属性经常修改,每次修改的话属性.属性会很长,所以希望引用另一简单对象,但data内this用不了。(集合数组是地址引用,基本数据类型这么操作没意义) 如: 解决办法…

数字信号处理及MATLAB仿真(3)——采样与量化

今天写主要来编的程序就是咱们AD变换的两个步骤。一个是采样,还有一个是量化。大家可以先看看,这一过程当中的信号是如何变化的。信号的变换图如下。 先说说采样,采样是将连续时间信号转换为离散时间信号的过程。在采样过程中,连续…

进程的控制-孤儿进程和僵尸进程

孤儿进程 : 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程( 进程号为 1) 所收养,并由 init 进程对它们完成状态收集工作 为了释放子进程的占用的系统资源: …

解决Linux环境Qt报“cannot find -lgl“问题

今天,在Ubuntu 18.04.6环境下,安装Qt5.14.2之后,运行一个QWidget工程,发现Qt报"cannot find -lgl"错误。     出现这种现象的原因:Qt的Path路径没有配置,缺少libqt4-dev依赖包和一些必要的组件…

拉曼光谱入门:2.拉曼光谱发展史、拉曼效应与试样温度的确定方法

1.拉曼光谱技术发展史 这里用简单的箭头与关键字来概括一下拉曼光谱技术的发展史 1928年:拉曼效应的发现 → 拉曼光谱术的初步应用20世纪40年代:红外光谱术的发展 → 拉曼光谱术的限制20世纪60年代:激光作为光源的引入 → 拉曼光谱术的性能提…

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01—短信/邮件/异常/MD5

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01 环境搭建验证码倒计时短信服务邮件服务验证码短信形式:邮件形式: 异常机制MD5参考 环境搭建 C:\Windows\System32\drivers\etc\hosts 192.168.…

使用flask的web网页部署介绍

使用flask的web网页部署介绍 文章目录 前言一、网页介绍二、数据库设计介绍总结 前言 flaskbootstrapjquerymysql搭建三叶青在线识别网站,使用nginxgunicorn将网站部署在腾讯云上,配置SSL证书。网站地址:https://www.whtuu.cn 三叶青图像识…

2024年6月后2周重要的大语言模型论文总结:LLM进展、微调、推理和对齐

本文总结了2024年6月后两周发表的一些最重要的大语言模型论文。这些论文涵盖了塑造下一代语言模型的各种主题,从模型优化和缩放到推理、基准测试和增强性能。 LLM进展与基准 1、 BigCodeBench: Benchmarking Code Generation with Diverse Function Calls and Com…

【C++】模板进阶--保姆级解析(什么是非类型模板参数?什么是模板的特化?模板的特化如何应用?)

目录 一、前言 二、什么是C模板? 💦泛型编程的思想 💦C模板的分类 三、非类型模板参数 ⚡问题引入⚡ ⚡非类型模板参数的使用⚡ 🔥非类型模板参数的定义 🔥非类型模板参数的两种类型 &#x1f52…

linux下高级IO模型

高级IO 1.高级IO模型基本概念1.1 阻塞IO1.2 非阻塞IO1.3 信号驱动IO1.4 IO多路转接1.5 异步IO 2. 模型代码实现2.1 非阻塞IO2.2 多路转接-selectselect函数介绍什么才叫就绪呢?demoselect特点 2.3 多路转接-pollpoll函数介绍poll优缺点demo 2.4 多路转接-epoll&…

为什么人一旦开窍了就变的特别厉害?

点击上方△腾阳 关注 《让子弹飞》这部电影非常经典,其中一个名场面就是“六子吃粉”。 电影里,胡万对着老六就是一顿狂轰滥炸:“吃了两碗粉,就给一碗的钱,你当咱这是慈善堂呢?” 老六一听,那…

SpringBoot+ELK 收集日志的两种方式

方式一、FileBeatlogstash 7.5.1(docker)ES(docker)springboot 日志文件 应用方式 我们采用ELFK 架构采集日志,直接读取日志生成的文件,不对Springboot的日志任何的修改。也就是FileBeat 通过读取日志文件位置获取日志内容,然后发送至logsta…

移动应用开发课设——原神小助手文档(1)

2023年末,做的移动应用开发课设,分还算高,项目地址:有帮助的话,点个赞和星呗~ GitHub - blhqwjs/-GenShin_imp: 2023年移动应用开发课设 本文按照毕业论文要求来写,希望对大家有所帮助。 xxxx大学课程设计报…

C++--partition库函数

介绍 在C中,partition函数通常是指STL(Standard Template Library)中的std::partition算法,它用于对一个序列进行分区操作。具体来说,std::partition接受一个范围和一个谓词(predicate)作为参数…

win10使用小技巧一

1. 查看电脑IP地址 步骤:按WinR打开运行框 → 输入cmd点确定 → 输入ipconfig回车 → 查看IP地址。 2. 解决网页文字不能复制 步骤:按F12 → 调试框里点击设置 → 向下滑找到 禁用 JavaScript → 勾选 → 复制文字。 3. 解决电脑不能上网 方法一&…

im即时通讯哪家好?WorkPlus im即时通讯集成底座为企业保驾护航

在当今数字化时代,即时通讯是企业内部沟通和协作的重要工具,提高工作效率和团队协作效果。在众多IM即时通讯提供商中,WorkPlus作为一家具有独特优势的企业IM即时通讯集成底座,为企业提供了全面的功能和安全保障,为企业…