深入掌握 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的通信流程进行详细解析:
-
定义IDL(接口定义语言)文件:通过Protobuf的
.proto文件,定义服务接口和消息格式,这是所有客户端与服务器通信的基础。 -
编译IDL文件:使用Protobuf编译器(
protoc),生成客户端与服务器的代码骨架。这一步使得开发者无需关心底层通信细节,大幅减少编码工作量。 -
客户端调用服务:客户端通过调用生成的骨架代码发起请求,Protobuf负责将消息进行序列化,并通过RPC框架的协议栈传输至服务器。
-
服务器处理请求:服务器接收客户端请求,反序列化数据,执行相应的业务逻辑,并将结果序列化为响应数据返回给客户端。
-
客户端接收响应:客户端接收到服务器的响应后,反序列化数据并继续处理后续业务逻辑。
二、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.h和user_service.pb.cc - 服务接口及骨架代码:
user_service.grpc.pb.h和user_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 编译与运行
-
安装依赖
确保已安装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 -
编译项目
在项目根目录下创建构建目录并编译:
mkdir build cd build cmake .. make -
运行服务器
在一个终端中启动服务器:
./server输出:
Server listening on 0.0.0.0:50051 -
运行客户端
在另一个终端中运行客户端:
./client输出:
User ID: 1 Name: Alice Email: alice@example.com
2.7 关键组件解析

以下是各组件在上述实现中的作用:
-
client(客户端):
- 客户端代码位于
client.cpp,通过UserServiceClient类发起RPC请求。 - serialize(序列化):Protobuf自动处理消息的序列化,将
GetUserRequest转换为字节流。 - deserialize(反序列化):接收
GetUserResponse字节流并转换为User对象。
- 客户端代码位于
-
server(服务器):
- 服务器代码位于
server.cpp,通过UserServiceImpl类实现服务逻辑。 - serialize/deserialize:Protobuf自动处理接收的请求数据反序列化和响应数据序列化。
- 服务器代码位于
-
protocol stack(协议栈):
- 在本示例中,gRPC基于HTTP/2协议栈处理网络通信细节,负责数据的传输和连接管理。
-
idl(接口定义语言):
user_service.proto文件定义了服务接口和消息结构,作为IDL文件。
-
compiler(编译器):
protoc编译器与gRPC插件一起,将.proto文件编译生成C++代码,生成消息类和服务骨架代码。
-
skeleton(骨架代码):
- 生成的
user_service.grpc.pb.h和user_service.grpc.pb.cc文件包含服务接口的骨架代码。 - 服务器实现类
UserServiceImpl继承自生成的骨架类,完成业务逻辑。 - 客户端通过生成的
UserService::Stub类进行远程调用,隐藏了底层的通信细节。
- 生成的
2.8 序列化与反序列化的实现
在上述示例中,序列化与反序列化过程由gRPC和Protobuf库自动处理,开发者无需手动编写相关代码。然而,理解这一过程对于优化和调试至关重要。
-
序列化:
- 客户端在调用
stub_->GetUser(&context, request, &response)时,Protobuf将GetUserRequest对象序列化为二进制格式,通过gRPC协议栈发送到服务器。
- 客户端在调用
-
反序列化:
- 服务器接收到字节流后,Protobuf将其反序列化为
GetUserRequest对象,并传递给GetUser方法进行处理。
- 服务器接收到字节流后,Protobuf将其反序列化为
-
响应过程:
- 服务器将
GetUserResponse对象序列化后,通过gRPC协议栈发送回客户端,客户端再将其反序列化为GetUserResponse对象。
- 服务器将
三、关键实现与解析
通过上述代码示例,我们可以看到Protobuf和RPC的高效结合体现在以下几个方面:
-
自动化生成代码:通过Protobuf定义的IDL文件(
.proto),可以自动生成C++类,省去手动编写通信代码的复杂性。 -
数据序列化与反序列化:Protobuf通过高效的二进制格式,减少了数据传输的开销,保证了性能和数据传输的一致性。
-
RPC框架的透明性:开发者不必关心底层通信协议,gRPC框架负责数据的传输和连接管理,使得程序员能够像调用本地函数一样完成远程调用。
四、工程实践中的最佳实践
-
代码生成自动化:集成Protobuf的代码生成步骤至构建系统,确保
.proto文件变更后,自动生成与其匹配的代码。 -
性能调优:对于大规模消息传输,可考虑使用Protobuf的流式解析功能,并优化网络带宽。
-
版本兼容性:在设计Protobuf消息结构时,尽量避免破坏兼容性,确保后续扩展性。例如,避免删除字段或改变字段编号。
-
错误处理机制:在客户端和服务器端实现完备的错误处理机制,确保网络异常和序列化失败时的回退策略。
五、总结
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 序列化与反序列化的实现 三、关键实现与解析四…...
录屏软件大比拼:四款必备工具助你轻松录制精彩瞬间!
哎呀,说到电脑录屏这事儿,我这个办公室小文员可是深有体会啊!平时工作里,经常需要录个会议啊、做个教程啊,或者分享个操作技巧给同事们看。市面上的录屏软件多得数不清,但我最常用的几款工具。今天就来跟大…...
计算机毕业设计宠物领养网站我的发布领养领养用户信息/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
目录 1.课题背景 2.课题意义 3.技术介绍 4.技术性需求 4.1后端服务: 4.2 前端展示 5.数据库设计: 6.系统性能: 7.安全性: 8. 功能介绍: 9. 部分代码 1.课题背景 近年来,随着宠物饲养数量…...
用示波器测动态滞回线
大学物理(下)实验-中南民族大学通信工程2022级 手动逐个处理数据较为麻烦且还要绘图,故想到用pythonmatplotlib来计算结果并数据可视化。 代码实现 import matplotlib.pyplot as plt# 样品一磁化曲线 X [0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2.…...
【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类
在Java开发中,JDK动态代理是一种非常有用的技术,它允许开发者在不修改目标类代码的情况下,为目标类添加额外的功能。然而,JDK动态代理的使用有一些限制,特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原…...
MFC工控项目实例二十一型号选择界面删除参数按钮禁用切换
承接专栏《MFC工控项目实例二十手动测试界面模拟量输入实时显示》 对于禁止使用的删除、参数按钮,在选中列表控件选项时切换为能够使用。 1、在TypDlg.h文件中添加代码 #include "ShadeButtonST.h" #include "BtnST.h" class CTypDlg : publi…...
前端框架对比和选择指南
前端框架对比和选择指南 随着 Web 开发技术的快速发展,前端框架已经成为了现代 Web 开发的核心工具之一。它们为开发人员提供了快速构建高效、交互性强的应用的基础。当前流行的前端框架主要包括 React.js、Vue.js 和 Angular.js。在这篇技术博客中,我们…...
人工智能价格战——如何降低成本让人工智能更易于普及
十年前,开发人工智能 (AI) 是只有大公司和资金充足的研究机构才能负担得起的事情。必要的硬件、软件和数据存储成本非常高。但从那时起,情况发生了很大变化。一切始于 2012 年的 AlexNet,这是一种深度学习模型,展示了神经网络的真…...
企业间图文档发放:如何在保障安全的同时提升效率?
不管是大型企业,还是小型创业公司,不论企业规模大小,每天都会有大量的图文档发放,对内传输协作和对外发送使用,数据的生产也是企业业务生产力的体现之一。 伴随着业务范围的不断扩大,企业与客户、合作伙伴之…...
深入解析 ConcurrentHashMap:从 JDK 1.7 到 JDK 1.8
✨探索Java基础 ConcurrentHashMap✨ 引言 ConcurrentHashMap 是 Java 中一个线程安全的高效 Map 集合。它在多线程环境下提供了高性能的数据访问和修改能力。本文将详细探讨 ConcurrentHashMap 在 JDK 1.7 和 JDK 1.8 中的不同实现方式,以及它们各自的优缺点。 …...
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? XPath(XML Path Language)是用于在XML(Extensible Markup Language)文档中查找信息的语言。它可以通过路径表达式来选择XML文档中的节点,类似于如何在文件系统中使用路径查找文件。XPath是…...
Visual Studio 2022
VS(Visual Studio)是一款由微软开发的集成开发环境(IDE),用于开发应用程序、网站以及移动应用等。VS的历史可以追溯到1997年,当时发布了第一个版本的VS。以下是VS的一些重要历史里程碑: Visual …...
微软Win11 22H2/23H2 九月可选更新KB5043145发布!
系统之家于9月27日发出最新报道,微软针对Windows11系统,发布了九月最新可选更新补丁KB5043145,22H2用户安装后,系统版本号升至22621.4249,23H2用户安装后升至22631.4249。本次更新修复了Edge使用IE模式有时会停止响应等…...
试试号称最好的7B模型(论文复现)
试试号称最好的7B模型(论文复现) 本文所涉及所有资源均在传知代码平台可获取 文章目录 试试号称最好的7B模型(论文复现)概述论文原理部署与复现推理微调adapter 融合 概述 Mistral 7B 是一个新型的具有 7.3 万亿参数的大语言模型。…...
CTF中文件包含
php伪协议的分类 伪协议是文件包含的基础,理解伪协议的原理才能更好的利用文件包含漏洞。 php://input php://input代表可以访问请求的原始数据,简单来说POST请求的情况下,php://input可以获取到post的数据。 使用条件:includ…...
20.指针相关知识点1
指针相关知识点1 1.定义一个指针变量指向数组2.指针偏移遍历数组3.指针偏移的补充4.指针和数组名的见怪不怪5.函数、指针、数组的结合 1.定义一个指针变量指向数组 指向数组首元素的地址 指向数组起始位置:等于数组名 #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` 的条件。然而,如果你仍然想在单节点集群中…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
