C++下Protobuf学习

news2025/6/8 21:16:19

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.coloradmin.cn/o/1902314.html

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

相关文章

前端面试题19(vue性能优化)

Vue.js应用的性能优化是一个多方面的过程,涉及初始化加载、运行时渲染以及用户交互等多个环节。以下是一些关键的Vue性能优化策略,包括详细的说明和示例代码: 1. 懒加载组件 对于大型应用,可以使用懒加载来减少初始加载时间。Vu…

前端必修技能:高手进阶核心知识分享 - CSS mix-blend-mode 图片混合模式详解

标签定义及使用说明 mix-blend-mode 属性描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合。 语法 mix-blend-mod: 使用mix-blend-mode 各种混合模式实例 注意: Internet Explorer 或 Edge 浏览器不支持 mix-blend-mode 属性。 (还是那个熟…

404白色唯美动态页面源码

404白色唯美动态页面源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 404白色唯美动态页面源码

【IT领域新生必看】深入浅出Java:值传递与引用传递的神奇区别

文章目录 引言什么是值传递?定义和使用值传递示例: 什么是引用传递?定义和使用引用传递示例: 值传递与引用传递的区别参数类型示例: 参数传递方式示例: 修改效果示例: 内存管理示例:…

antd实现简易相册,zdppy+vue3+antd实现前后端分离相册

前端代码 <template><a-image:preview"{ visible: false }":width"200"src"http://localhost:8889/download/1.jpg"click"visible true"/><div style"display: none"><a-image-preview-group:previe…

WACV2023论文速览Attention注意力机制相关

Paper1 ScoreNet: Learning Non-Uniform Attention and Augmentation for Transformer-Based Histopathological Image Classification 摘要原文: Progress in digital pathology is hindered by high-resolution images and the prohibitive cost of exhaustive localized an…

移动硬盘“需格式化”危机:应对策略与数据拯救指南

移动硬盘困境&#xff1a;突如其来的“格式化”提示 在日常的数据存储与传输过程中&#xff0c;移动硬盘作为便携且容量可观的存储媒介&#xff0c;深受用户青睐。然而&#xff0c;当这块存储“小能手”突然弹出“需要格式化”的警告时&#xff0c;无疑给用户的数据安全敲响了…

rancher管理多个集群

一、rancher部署 单独部署到一台机器上&#xff0c;及独立于k8s集群之外&#xff1a; 删除所有yum源&#xff0c;重新建yum源&#xff1a; # 建centos7.9的yum源 # cat CentOS-Base.repo # CentOS-Base.repo # # The mirror system uses the connecting IP address of the …

基于SpringBoot的校园台球厅人员与设备管理系统

本系统是要设计一个校园台球厅人员与设备管理系统&#xff0c;这个系统能够满足校园台球厅人员与设备的管理及用户的校园台球厅人员与设备管理功能。系统的主要功能包括首页、个人中心、用户管理、会员账号管理、会员充值管理、球桌信息管理、会员预约管理、普通预约管理、留言…

MSI安装包安装的Mysql8,配置文件my.ini在哪儿?

版本 我安装的版本是8.0.36&#xff0c;server根目录下没有配置文件。 文件位置 首先找到对应的windows服务 右击属性&#xff0c;可以看到启动参数&#xff0c;启动参数中有配置文件的路径 比如我的配置文件在"C:\ProgramData\MySQL\MySQL Server 8.0\my.ini"

STM32 - 内存分区与OTA

最近搞MCU&#xff0c;发现它与SOC之间存在诸多差异&#xff0c;不能沿用SOC上一些技术理论。本文以STM L4为例&#xff0c;总结了一些STM32 小白入门指南。 标题MCU没有DDR&#xff1f; 是的。MCU并没有DDR&#xff0c;而是让代码存储在nor flash上&#xff0c;临时变量和栈…

2024年导游资格证题库备考题库,高效备考!

1.台湾著名的太鲁阁公园的特色是&#xff08;&#xff09;。 A.丘陵和溶洞 B.森林和瀑布 C.峡谷和断崖 D.彩林和彩池 答案&#xff1a;C 解析&#xff1a;台湾著名的太鲁阁公园的特色是峡谷和断崖。 2.下列位于台湾的景区中&#xff0c;素有"神秘的森林王国"之…

js好用的动态分页插件

js好用的动态分页插件是一款简单的分页样式插件&#xff0c;支持样式类型&#xff0c;当前页&#xff0c;每页显示数量&#xff0c;按钮数量&#xff0c;总条数&#xff0c;上一页文字&#xff0c;下一页文字&#xff0c;输入框跳转等功能。 js好用的动态分页插件

如何从相机的存储卡中恢复原始照片

“不好了。” 当您意识到自己不小心从存储卡中删除了照片&#xff0c;或者错误地格式化了相机的记忆棒时&#xff0c;您首先会喊出这两个词。这是一种常见的情况&#xff0c;每个人一生中都会遇到这种情况。幸运的是&#xff0c;有办法从相机的 RAW 记忆棒中恢复已删除的照片。…

Android在framework层添加自定义服务的流程

环境说明 ubuntu16.04android4.1java version “1.6.0_45”GNU Make 3.81gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 可能有人会问&#xff0c;现在都2024了怎么还在用android4版本&#xff0c;早都过时了。确实&#xff0c;现在最新的都是Android13、And…

Linux笔记之二

Linux笔记之二 一、文件属性学习二、软链接和硬链接1.软链接2.硬链接 三、Vim编辑器四、账号管理总结 一、文件属性学习 Linux 系统是一种典型的多用户系统&#xff0c;不同的用户处于不同的地位&#xff0c;拥有不同的权限。为了保护系统的安全性&#xff0c;Linux系统对不同…

Educational Codeforces Round 167(Div.2) A~D

A.Catch the Coin&#xff08;思维&#xff09; 题意&#xff1a; Monocarp 参观了一家有街机柜的复古街机俱乐部。在那里&#xff0c;他对"抓硬币"游戏机产生了好奇。 游戏非常简单。屏幕上的坐标网格是这样的 X X X轴从左到右&#xff1b; Y Y Y轴从下往上&…

web缓存代理服务器

一、web缓存代理 web代理的工作机制 代理服务器是一个位于客户端和原始&#xff08;资源&#xff09;服务器之间的服务器&#xff0c;为了从原始服务器取得内容&#xff0c;客户端向代理服务器发送一个请求&#xff0c;并指定目标原始服务器&#xff0c;然后代理服务器向原始…

Python基础小知识问答系列-自定义反向迭代器

1. 问题&#xff1a; 现在想要一个具有反向迭代的迭代器&#xff0c;该怎样实现反向迭代的能力&#xff1f; 2. 解决方法&#xff1a; 首先&#xff0c;在自定义一个迭代器&#xff0c;在类中实现__reversed__方法&#xff1b;使用内置函数reversed实现反 向迭代。 示例&…

linux 下,Java由Java8升级到Java11,Java不更新版本号

在ES对接过程&#xff0c;springboot3进行对接&#xff0c;需要将Java升级到11版本。首先下载安装选好的11版本Java&#xff0c;在linux下解压后&#xff0c;配置/etc/profile export JAVA_HOME/root/SJL/jdk-11.0.22 然后保存&#xff0c;执行文件source /etc/profile&#…