当前位置: 首页 > news >正文

深入掌握 Protobuf 与 RPC 的高效结合:实现C++工程中的高效通信

目录

      • 一、Protobuf与RPC框架的通信流程概述
      • 二、Protobuf与RPC在C++中的实际应用
        • 2.1 定义 `.proto` 文件
        • 2.2 编译 `.proto` 文件生成C++代码
        • 2.3 实现服务器端逻辑
        • 2.4 实现客户端逻辑
        • 2.5 使用CMake构建工程
        • 2.6 编译与运行
        • 2.7 关键组件解析
        • 2.8 序列化与反序列化的实现
      • 三、关键实现与解析
      • 四、工程实践中的最佳实践
      • 五、总结
      • 附录:常用Protobuf与gRPC命令
      • 参考资料

在现代分布式系统中,高效的通信至关重要,而Protobuf(Protocol Buffers)不仅能提供数据序列化的高效性,还常被用于与RPC(远程过程调用)框架结合,优化客户端与服务器之间的通信效率。本文将详细解析如何在C++工程中使用Protobuf,并结合RPC框架,构建高性能的分布式系统通信机制。

一、Protobuf与RPC框架的通信流程概述

在这里插入图片描述

为了便于理解,以下结合Protobuf和RPC的通信流程进行详细解析:

  1. 定义IDL(接口定义语言)文件:通过Protobuf的.proto文件,定义服务接口和消息格式,这是所有客户端与服务器通信的基础。

  2. 编译IDL文件:使用Protobuf编译器(protoc),生成客户端与服务器的代码骨架。这一步使得开发者无需关心底层通信细节,大幅减少编码工作量。

  3. 客户端调用服务:客户端通过调用生成的骨架代码发起请求,Protobuf负责将消息进行序列化,并通过RPC框架的协议栈传输至服务器。

  4. 服务器处理请求:服务器接收客户端请求,反序列化数据,执行相应的业务逻辑,并将结果序列化为响应数据返回给客户端。

  5. 客户端接收响应:客户端接收到服务器的响应后,反序列化数据并继续处理后续业务逻辑。

二、Protobuf与RPC在C++中的实际应用

2.1 定义 .proto 文件

首先,我们需要通过Protobuf定义服务接口和消息结构。例如,下面的.proto文件定义了一个简单的用户服务接口,包括获取用户信息的服务:

syntax = "proto3";package example;message User {int32 id = 1;string name = 2;string email = 3;
}message GetUserRequest {int32 id = 1;
}message GetUserResponse {User user = 1;
}service UserService {rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
2.2 编译 .proto 文件生成C++代码

使用protoc编译器生成C++代码:

protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto

编译后生成的文件包含以下两部分:

  • 消息类user_service.pb.huser_service.pb.cc
  • 服务接口及骨架代码user_service.grpc.pb.huser_service.grpc.pb.cc
2.3 实现服务器端逻辑

服务器端需要实现生成的服务接口,并通过RPC服务器处理客户端的请求。下面是基于gRPC的C++服务器实现:

#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "user_service.grpc.pb.h"using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using example::UserService;
using example::GetUserRequest;
using example::GetUserResponse;
using example::User;class UserServiceImpl final : public UserService::Service {
public:Status GetUser(ServerContext* context, const GetUserRequest* request,GetUserResponse* reply) override {// 模拟数据库操作User* user = reply->mutable_user();user->set_id(request->id());user->set_name("Alice");user->set_email("alice@example.com");return Status::OK;}
};void RunServer() {std::string server_address("0.0.0.0:50051");UserServiceImpl service;ServerBuilder builder;builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());builder.RegisterService(&service);std::unique_ptr<Server> server(builder.BuildAndStart());std::cout << "Server listening on " << server_address << std::endl;server->Wait();
}int main() {RunServer();return 0;
}
2.4 实现客户端逻辑

客户端通过调用生成的代码与服务器进行通信。以下是gRPC客户端的实现代码:

#include <iostream>
#include <memory>
#include <grpcpp/grpcpp.h>
#include "user_service.grpc.pb.h"using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using example::UserService;
using example::GetUserRequest;
using example::GetUserResponse;class UserServiceClient {
public:UserServiceClient(std::shared_ptr<Channel> channel): stub_(UserService::NewStub(channel)) {}GetUserResponse GetUser(int id) {GetUserRequest request;request.set_id(id);GetUserResponse response;ClientContext context;Status status = stub_->GetUser(&context, request, &response);if (!status.ok()) {std::cerr << "RPC failed: " << status.error_message() << std::endl;}return response;}private:std::unique_ptr<UserService::Stub> stub_;
};int main() {UserServiceClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));int user_id = 1;GetUserResponse response = client.GetUser(user_id);std::cout << "User ID: " << response.user().id() << std::endl;std::cout << "Name: " << response.user().name() << std::endl;std::cout << "Email: " << response.user().email() << std::endl;return 0;
}
2.5 使用CMake构建工程

使用CMake构建Protobuf和gRPC项目,确保将生成的代码自动编译到项目中:

cmake_minimum_required(VERSION 3.14)
project(ProtobufRPCExample)find_package(Protobuf REQUIRED)
find_package(gRPC REQUIRED)set(CMAKE_CXX_STANDARD 14)protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS user_service.proto)
grpc_generate_cpp(GRPC_SRCS GRPC_HDRS user_service.proto)add_executable(server server.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS})
target_link_libraries(server PRIVATE protobuf::libprotobuf gRPC::grpc++)add_executable(client client.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS})
target_link_libraries(client PRIVATE protobuf::libprotobuf gRPC::grpc++)
2.6 编译与运行
  1. 安装依赖

    确保已安装Protobuf和gRPC库。可以参考以下步骤进行安装:

    # 安装gRPC及其依赖
    git clone -b v1.53.0 https://github.com/grpc/grpc
    cd grpc
    git submodule update --init
    mkdir -p build
    cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
    make -j4
    sudo make install
    sudo ldconfig
    
  2. 编译项目

    在项目根目录下创建构建目录并编译:

    mkdir build
    cd build
    cmake ..
    make
    
  3. 运行服务器

    在一个终端中启动服务器:

    ./server
    

    输出:

    Server listening on 0.0.0.0:50051
    
  4. 运行客户端

    在另一个终端中运行客户端:

    ./client
    

    输出:

    User ID: 1
    Name: Alice
    Email: alice@example.com
    
2.7 关键组件解析

在这里插入图片描述
以下是各组件在上述实现中的作用:

  1. client(客户端)

    • 客户端代码位于 client.cpp,通过 UserServiceClient 类发起RPC请求。
    • serialize(序列化):Protobuf自动处理消息的序列化,将 GetUserRequest 转换为字节流。
    • deserialize(反序列化):接收 GetUserResponse 字节流并转换为 User 对象。
  2. server(服务器)

    • 服务器代码位于 server.cpp,通过 UserServiceImpl 类实现服务逻辑。
    • serialize/deserialize:Protobuf自动处理接收的请求数据反序列化和响应数据序列化。
  3. protocol stack(协议栈)

    • 在本示例中,gRPC基于HTTP/2协议栈处理网络通信细节,负责数据的传输和连接管理。
  4. idl(接口定义语言)

    • user_service.proto 文件定义了服务接口和消息结构,作为IDL文件。
  5. compiler(编译器)

    • protoc 编译器与gRPC插件一起,将.proto文件编译生成C++代码,生成消息类和服务骨架代码。
  6. skeleton(骨架代码)

    • 生成的 user_service.grpc.pb.huser_service.grpc.pb.cc 文件包含服务接口的骨架代码。
    • 服务器实现类 UserServiceImpl 继承自生成的骨架类,完成业务逻辑。
    • 客户端通过生成的 UserService::Stub 类进行远程调用,隐藏了底层的通信细节。
2.8 序列化与反序列化的实现

在上述示例中,序列化与反序列化过程由gRPC和Protobuf库自动处理,开发者无需手动编写相关代码。然而,理解这一过程对于优化和调试至关重要。

  • 序列化

    • 客户端在调用 stub_->GetUser(&context, request, &response) 时,Protobuf将 GetUserRequest 对象序列化为二进制格式,通过gRPC协议栈发送到服务器。
  • 反序列化

    • 服务器接收到字节流后,Protobuf将其反序列化为 GetUserRequest 对象,并传递给 GetUser 方法进行处理。
  • 响应过程

    • 服务器将 GetUserResponse 对象序列化后,通过gRPC协议栈发送回客户端,客户端再将其反序列化为 GetUserResponse 对象。

三、关键实现与解析

通过上述代码示例,我们可以看到Protobuf和RPC的高效结合体现在以下几个方面:

  1. 自动化生成代码:通过Protobuf定义的IDL文件(.proto),可以自动生成C++类,省去手动编写通信代码的复杂性。

  2. 数据序列化与反序列化:Protobuf通过高效的二进制格式,减少了数据传输的开销,保证了性能和数据传输的一致性。

  3. RPC框架的透明性:开发者不必关心底层通信协议,gRPC框架负责数据的传输和连接管理,使得程序员能够像调用本地函数一样完成远程调用。

四、工程实践中的最佳实践

  1. 代码生成自动化:集成Protobuf的代码生成步骤至构建系统,确保.proto文件变更后,自动生成与其匹配的代码。

  2. 性能调优:对于大规模消息传输,可考虑使用Protobuf的流式解析功能,并优化网络带宽。

  3. 版本兼容性:在设计Protobuf消息结构时,尽量避免破坏兼容性,确保后续扩展性。例如,避免删除字段或改变字段编号。

  4. 错误处理机制:在客户端和服务器端实现完备的错误处理机制,确保网络异常和序列化失败时的回退策略。

五、总结

Protobuf与RPC框架的结合为现代分布式系统提供了一种高效的通信方案。在C++工程中,通过合理设计和使用Protobuf进行数据序列化,再借助gRPC完成远程过程调用,可以大幅提升通信效率,并简化系统的开发与维护。

附录:常用Protobuf与gRPC命令

  • 编译Protobuf文件

    protoc --cpp_out=. user_service.proto
    
  • 编译gRPC Protobuf文件

    protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto
    
  • 生成所有代码(包括消息和gRPC服务)

    protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto
    

参考资料

  • Protocol Buffers 官方文档
  • gRPC 官方文档
  • Protobuf 编码原理详解
  • 0voice · GitHub

相关文章:

深入掌握 Protobuf 与 RPC 的高效结合:实现C++工程中的高效通信

目录 一、Protobuf与RPC框架的通信流程概述二、Protobuf与RPC在C中的实际应用2.1 定义 .proto 文件2.2 编译 .proto 文件生成C代码2.3 实现服务器端逻辑2.4 实现客户端逻辑2.5 使用CMake构建工程2.6 编译与运行2.7 关键组件解析2.8 序列化与反序列化的实现 三、关键实现与解析四…...

录屏软件大比拼:四款必备工具助你轻松录制精彩瞬间!

哎呀&#xff0c;说到电脑录屏这事儿&#xff0c;我这个办公室小文员可是深有体会啊&#xff01;平时工作里&#xff0c;经常需要录个会议啊、做个教程啊&#xff0c;或者分享个操作技巧给同事们看。市面上的录屏软件多得数不清&#xff0c;但我最常用的几款工具。今天就来跟大…...

计算机毕业设计宠物领养网站我的发布领养领养用户信息/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

目录 1.课题背景 2.课题意义 ‌ 3.技术介绍 4.技术性需求 4.1后端服务‌&#xff1a; 4.2 前端展示‌ 5.数据库设计‌&#xff1a; 6.系统性能‌&#xff1a; 7.安全性‌&#xff1a; 8. 功能介绍&#xff1a; 9. 部分代码 1.课题背景 近年来&#xff0c;随着宠物饲养数量…...

用示波器测动态滞回线

大学物理&#xff08;下&#xff09;实验-中南民族大学通信工程2022级 手动逐个处理数据较为麻烦且还要绘图&#xff0c;故想到用pythonmatplotlib来计算结果并数据可视化。 代码实现 import matplotlib.pyplot as plt# 样品一磁化曲线 X [0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2.…...

【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类

在Java开发中&#xff0c;JDK动态代理是一种非常有用的技术&#xff0c;它允许开发者在不修改目标类代码的情况下&#xff0c;为目标类添加额外的功能。然而&#xff0c;JDK动态代理的使用有一些限制&#xff0c;特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原…...

MFC工控项目实例二十一型号选择界面删除参数按钮禁用切换

承接专栏《MFC工控项目实例二十手动测试界面模拟量输入实时显示》 对于禁止使用的删除、参数按钮&#xff0c;在选中列表控件选项时切换为能够使用。 1、在TypDlg.h文件中添加代码 #include "ShadeButtonST.h" #include "BtnST.h" class CTypDlg : publi…...

前端框架对比和选择指南

前端框架对比和选择指南 随着 Web 开发技术的快速发展&#xff0c;前端框架已经成为了现代 Web 开发的核心工具之一。它们为开发人员提供了快速构建高效、交互性强的应用的基础。当前流行的前端框架主要包括 React.js、Vue.js 和 Angular.js。在这篇技术博客中&#xff0c;我们…...

人工智能价格战——如何降低成本让人工智能更易于普及

十年前&#xff0c;开发人工智能 (AI) 是只有大公司和资金充足的研究机构才能负担得起的事情。必要的硬件、软件和数据存储成本非常高。但从那时起&#xff0c;情况发生了很大变化。一切始于 2012 年的 AlexNet&#xff0c;这是一种深度学习模型&#xff0c;展示了神经网络的真…...

企业间图文档发放:如何在保障安全的同时提升效率?

不管是大型企业&#xff0c;还是小型创业公司&#xff0c;不论企业规模大小&#xff0c;每天都会有大量的图文档发放&#xff0c;对内传输协作和对外发送使用&#xff0c;数据的生产也是企业业务生产力的体现之一。 伴随着业务范围的不断扩大&#xff0c;企业与客户、合作伙伴之…...

深入解析 ConcurrentHashMap:从 JDK 1.7 到 JDK 1.8

✨探索Java基础 ConcurrentHashMap✨ 引言 ConcurrentHashMap 是 Java 中一个线程安全的高效 Map 集合。它在多线程环境下提供了高性能的数据访问和修改能力。本文将详细探讨 ConcurrentHashMap 在 JDK 1.7 和 JDK 1.8 中的不同实现方式&#xff0c;以及它们各自的优缺点。 …...

VS code user setting 与 workspace setting 的区别

VS code user setting 与 workspace setting 的区别 引言正文引言 相信有不少开始接触 VS code 的小伙伴会有疑问,user setting 与 workspace setting 有什么区别呢?这里我们来说明一下 正文 首先,当我们使用 Ctrl + Shift + P 打开搜索输入 setting 后,可以弹出 4 个se…...

XPath基础知识点讲解——用于在XML中查找信息的语言

1. 什么是XPath&#xff1f; XPath&#xff08;XML Path Language&#xff09;是用于在XML&#xff08;Extensible Markup Language&#xff09;文档中查找信息的语言。它可以通过路径表达式来选择XML文档中的节点&#xff0c;类似于如何在文件系统中使用路径查找文件。XPath是…...

Visual Studio 2022

VS&#xff08;Visual Studio&#xff09;是一款由微软开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;用于开发应用程序、网站以及移动应用等。VS的历史可以追溯到1997年&#xff0c;当时发布了第一个版本的VS。以下是VS的一些重要历史里程碑&#xff1a; Visual …...

微软Win11 22H2/23H2 九月可选更新KB5043145发布!

系统之家于9月27日发出最新报道&#xff0c;微软针对Windows11系统&#xff0c;发布了九月最新可选更新补丁KB5043145&#xff0c;22H2用户安装后&#xff0c;系统版本号升至22621.4249&#xff0c;23H2用户安装后升至22631.4249。本次更新修复了Edge使用IE模式有时会停止响应等…...

试试号称最好的7B模型(论文复现)

试试号称最好的7B模型&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 试试号称最好的7B模型&#xff08;论文复现&#xff09;概述论文原理部署与复现推理微调adapter 融合 概述 Mistral 7B 是一个新型的具有 7.3 万亿参数的大语言模型。…...

CTF中文件包含

php伪协议的分类 伪协议是文件包含的基础&#xff0c;理解伪协议的原理才能更好的利用文件包含漏洞。 php://input php://input代表可以访问请求的原始数据&#xff0c;简单来说POST请求的情况下&#xff0c;php://input可以获取到post的数据。 使用条件&#xff1a;includ…...

20.指针相关知识点1

指针相关知识点1 1.定义一个指针变量指向数组2.指针偏移遍历数组3.指针偏移的补充4.指针和数组名的见怪不怪5.函数、指针、数组的结合 1.定义一个指针变量指向数组 指向数组首元素的地址 指向数组起始位置&#xff1a;等于数组名 #include <stdio.h>int main(){int ar…...

PFC和LLC的本质和为什么要用PFC和LLC电路原因

我们可以用电感和电容的特性,以及电压和电流之间的不同步原理来解释PFC(功率因数校正)和LLC(谐振变换器)。 电感和电容的基本概念 电感(Inductor): 电感是一种储存电能的组件。它的电流变化比较慢,电流在电感中延迟,而电压变化得比较快。可以把电感想象成一个“滞后…...

自定义认证过滤器和自定义授权过滤器

目录 通过数据库动态加载用户信息 具体实现步骤 一.创建数据库 二.编写secutity配置类 三.编写controller 四.编写服务类实现UserDetailsService接口类 五.debug springboot启动类 认证过滤器 SpringSecurity内置认证流程 自定义认证流程 第一步:自定义一个类继承Abstr…...

单节点集群的设置及数据写入

背景:elasticsearch单个node节点写入数据-CSDN博客 单个节点数据,如下设置参数, 在单节点集群中,设置 `gateway.recover_after_nodes` 通常是没有意义的,因为单节点集群只有一个节点,无法满足 `gateway.recover_after_nodes` 的条件。然而,如果你仍然想在单节点集群中…...

【Linux学习】【Ubuntu入门】1-2 新建虚拟机ubuntu环境

1.双击打开VMware软件&#xff0c;点击“创建新的虚拟机”&#xff0c;在弹出的中选择“自定义&#xff08;高级&#xff09;” 2.点击下一步&#xff0c;自动识别ubuntu光盘映像文件&#xff0c;也可以点击“浏览”手动选择&#xff0c;点击下一步 3.设置名称及密码后&#xf…...

自动驾驶系列—自动驾驶MCU架构全方位解析:从单核到多核的选型指南与应用实例

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…...

基于单片机多功能称重系统设计

** 文章目录 前言概要功能设计设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对…...

PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker

文章目录 引言I 什么是 PWA功能特性技术上分为三个部分安装应用II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service Worker( 缓存管理)IV 结合构建工具让项目支持 PWA应用使用插件vite-plugin-pwaworkbox-webpack-plugin插件扩展知识将 PWA 作为脱机…...

【学习笔记】手写 Tomcat 八

目录 一、NIO 1. 创建 Tomcat NIO 类 2. 启动 Tomcat 3. 测试 二、解析请求信息 三、响应数据 创建响应类 修改调用的响应类 四、完整代码 五、测试 六、总结 七、获取全部用户的功能 POJO 生成 POJO 1. 在 Dao 层定义接口 2. 获取用户数据 3. 在 Service 层定…...

24年九月份生活随笔

九月份最后一天&#xff0c;烈士纪念日。 上午看了一会儿直播&#xff0c;庄重的仪式&#xff0c;铭记先辈为新中国抛头颅洒热血&#xff0c;当今盛世&#xff0c;如您所愿。 郑州马拉松官方通告&#xff0c;今天十点公布直通&#xff0c;中签&#xff0c;候补结果。 看完直…...

[含文档+PPT+源码等]精品大数据项目-基于Django实现的高校图书馆智能推送系统的设计与实现

大数据项目——基于Django实现的高校图书馆智能推送系统的设计与实现背景&#xff0c;可以从以下几个方面进行详细阐述&#xff1a; 一、信息技术的发展背景 随着信息技术的飞速发展和互联网的广泛普及&#xff0c;大数据已经成为现代社会的重要资源。在大数据背景下&#xf…...

Leecode刷题之路第七天之整数反转

题目出处 07-整数反转 题目描述 个人解法 思路&#xff1a; 1.将整数转换为字符串 2.倒序输出字符串 3.兼容负数case 代码示例&#xff1a;&#xff08;Java&#xff09; public int reverse(int x) {Integer integer new Integer(x);String s integer.toString();Strin…...

SpringBoot项目 | 瑞吉外卖 | 短信发送验证码功能改为免费的邮箱发送验证码功能 | 代码实现

0.前情提要 之前的po已经说了单独的邮箱验证码发送功能怎么实现&#xff1a; https://blog.csdn.net/qq_61551948/article/details/142641495 这篇说下如何把该功能整合到瑞吉项目里面&#xff0c;也就是把原先项目里的短信发送验证码的功能改掉&#xff0c;改为邮箱发送验证…...

Windows暂停更新

目录 前言注册表设定参考 前言 不想Windows自动更新&#xff0c;同时不想造成Windows商店不可用&#xff0c;可以采用暂停更新的方案。 但是通过这里设定的时间太短了&#xff0c;所以我们去注册表设定。 注册表设定 win r 输入 regedit进入注册表 HKEY_LOCAL_MACHINE\SOFT…...