【C++版本】protobuf与gRPC
文章目录
- 一、Protobuf
- 二、安装以及使用protoc
- 三、gRPC
- 1.Q&A
- 2.学习版rpc
- 3.gRPC压缩算法
- 参考
一、Protobuf

Google Protocol Buffers(protobuf)是一种语言中立、平台中立的序列化协议,旨在高效地将结构化数据进行序列化和反序列化。它主要用于通信协议、数据存储和其他需要高效编码和解码结构化数据的场景。protobuf 由 Google 开发和开源,广泛用于 Google 的内部系统以及众多开源项目和商业应用中。
Protobuf 的用途
(1)数据序列化:
- Protobuf 将数据结构化为紧凑的二进制格式,适用于网络传输、持久化存储等需要高效数据编码的场景。
- 相较于 XML 和 JSON,protobuf 编码后的数据占用更少的空间,解析速度更快。
- 跨语言和跨平台通信:
(2)Protobuf 支持多种编程语言,如 C++, Java, Python, Go, Ruby 等,适用于异构系统间的通信。
- 数据结构定义在 .proto 文件中,不同语言的代码生成器可以从 .proto 文件生成相应语言的类,实现数据的编解码。
- 远程过程调用(RPC):
(3)Protobuf 可以与 gRPC 结合使用,定义和实现高效的 RPC 协议,支持流式传输和双向通信。
- gRPC 通过 protobuf 定义服务接口和消息格式,自动生成客户端和服务端代码,简化了分布式系统的开发。
(4)数据存储:
- Protobuf 可以用于将数据序列化后存储到文件或数据库中,确保数据存储和传输的高效性。
- 由于 protobuf 的二进制格式紧凑,特别适合在存储空间有限或网络带宽受限的环境中使用。
(5)Protobuf 的优点
- 高效性:序列化后的数据格式紧凑,占用更少的存储空间和带宽,解析速度快。
- 可扩展性:支持向后兼容和向前兼容,允许在不破坏现有数据格式的情况下添加新字段。
- 多语言支持:生成的代码可在多种编程语言中使用,便于不同语言系统之间的数据交换。
- 简洁性:定义数据结构的 .proto 文件简单直观,便于维护和管理。
protobuffer C++基础见
二、安装以及使用protoc
$ apt install -y protobuf-compiler
$ protoc --version # Ensure compiler version is 3+
定义person.proto文件
//person.proto
package yaojun;message Person {required string name = 1;required int32 id = 2;optional string email = 3;
}
语法规则,字段定义:
每个字段有三部分:修饰符、类型和字段名,以及一个唯一的编号。
修饰符:
required:表示字段是必需的,消息必须包含该字段,否则解析消息时会报错。
optional:表示字段是可选的,消息中可以包含也可以不包含该字段。
repeated(示例中未使用):表示字段可以重复零次或多次,通常用于列表或数组。
类型:
string:表示字符串类型。
int32:表示32位整数类型。
字段名和编号:
每个字段有一个唯一的编号,用于标识字段。这些编号在消息的二进制表示中非常重要,用于解码数据。
编号必须是正整数,且在同一消息类型中必须唯一。
在这个例子中:
- required string name = 1;:定义了一个必需的字符串字段 name,编号为 1。
- required int32 id = 2;:定义了一个必需的 32 位整数字段 id,编号为 2。
- optional string email = 3;:定义了一个可选的字符串字段 email,编号为 3。
~/code/PremiumProject/protobuf main
protoc --proto_path=. --cpp_out=. person.proto
代码:
// yaojun_person.cpp
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
#include "person.pb.h"
#include <fstream>
#include <iostream>using namespace yaojun;
int main() {Person p;p.set_name("test");p.set_id(100);p.set_email("940334249@qq.com");// 将pb二进制信息保存到字符串, 序列化std::string str;p.SerializeToString(&str);std::cout << "str: [" << str << "]" << std::endl;// 将pb文本信息写入文件std::ofstream fw;fw.open("./Person.txt", std::ios::out | std::ios::binary);google::protobuf::io::OstreamOutputStream *output =new google::protobuf::io::OstreamOutputStream(&fw);google::protobuf::TextFormat::Print(p, output);delete output;fw.close();// 将pb文本信息保存到字符串std::string str1;google::protobuf::TextFormat::PrintToString(p, &str1);std::cout << "str1: [" << str1 << "]" << std::endl;// 反序列化Person p1;p1.ParseFromString(str);std::cout << "name:" << p1.name() << ",email:" << p1.email()<< ",id:" << p1.id() << std::endl;return 0;
}
更好的例子见
三、gRPC
安装
~/code/PremiumProject/protobuf main
sudo apt-get install -y protobuf-compiler-grpc查看版本
grpc_cpp_plugin --version
grpc以及grpc++开发库
~/code/PremiumProject/grpc main
apt-get install -y libgrpc++-dev
定义一个rpc method:
syntax = "proto3";package calculator;service Calculator {rpc Add (AddRequest) returns (AddResponse);
}message AddRequest {int32 operand1 = 1;int32 operand2 = 2;
}message AddResponse {int32 result = 1;
}
解释:
syntax = “proto3”;
这行代码指定了使用 Protocol Buffers 的第 3 版语法(proto3)。proto3 是 Protocol Buffers 的一种更简洁和现代化的语法,与 proto2 相比,简化了许多功能和语法。
package calculator;
这行代码定义了包名 calculator。包名用于在生成代码时为不同的消息和服务提供命名空间,避免命名冲突。
service Calculator {
rpc Add (AddRequest) returns (AddResponse);
}
这段代码定义了一个名为 Calculator 的 gRPC 服务。
service 关键字用于定义一个服务,服务包含一个或多个远程过程调用(RPC)方法。
在这个例子中,Calculator 服务包含一个名为 Add 的 RPC 方法。
Add 方法接受一个 AddRequest 消息作为输入参数,并返回一个 AddResponse 消息作为结果。
rpc 关键字用于定义一个远程过程调用。
message AddRequest {
int32 operand1 = 1;
int32 operand2 = 2;
}
这段代码定义了一个名为 AddRequest 的消息类型。
message 关键字用于定义消息类型。
AddRequest 消息包含两个字段:operand1 和 operand2,都是 int32 类型。
每个字段有一个唯一的编号,用于在消息的二进制表示中标识字段。
message AddResponse {
int32 result = 1;
}
这段代码定义了一个名为 AddResponse 的消息类型。
AddResponse 消息包含一个字段:result,类型为 int32。
字段 result 的编号是 1。
构建:
#-I=.:指定 .proto 文件的包含路径。这意味着 protoc 在当前目录下查找 add.proto 文件。
#生成add.grpc.pb.h和add.grpc.pb.cc的gRPC代码
#--plugin=protoc-gen-grpc=which grpc_cpp_plugin``:指定使用 gRPC 插件 grpc_cpp_plugin 来生成 gRPC 相关代码
$ protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` add.proto#-I=.:指定 .proto 文件的包含路径。这意味着 protoc 在当前目录下查找 add.proto 文件。
#--cpp_out=.:指定生成的 C++ 代码的输出目录为当前目录。生成的 Protocol Buffers 数据结构代码将放在当前目录下。
# 生成add.pb.h,add.pb.cc的protobuffer 代码
protoc -I=. --cpp_out=. add.proto
计算器服务端代码:
#include <iostream>
#include <grpcpp/grpcpp.h>
#include "add.grpc.pb.h"using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;class CalculatorServiceImpl final : public Calculator::Service {
public:Status Add(ServerContext* context, const AddRequest* request, AddResponse* response) override {int result = request->operand1() + request->operand2();response->set_result(result);return Status::OK;}
};void RunServer() {std::string server_address("0.0.0.0:50052");CalculatorServiceImpl 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;
}
计算器客户端代码:
#include "add.grpc.pb.h"
#include <grpcpp/grpcpp.h>
#include <iostream>
#include <memory>using calculator::AddRequest;
using calculator::AddResponse;
using calculator::Calculator;
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;class CalculatorClient {
public:CalculatorClient(std::shared_ptr<Channel> channel): stub_(Calculator::NewStub(channel)) {}int Add(int operand1, int operand2) {AddRequest request;request.set_operand1(operand1);request.set_operand2(operand2);AddResponse response;ClientContext context;Status status = stub_->Add(&context, request, &response);if (status.ok()) {return response.result();} else {std::cout << "RPC failed: " << status.error_code() << ": "<< status.error_message() << std::endl;return -1;}}private:std::unique_ptr<Calculator::Stub> stub_;
};int main() {CalculatorClient calculator(grpc::CreateChannel("localhost:50052", grpc::InsecureChannelCredentials()));int result = calculator.Add(10, 20);if (result >= 0) {std::cout << "Result: " << result << std::endl;}return 0;
}
编译:
cmake in ubuntu20.04
cmake_minimum_required(VERSION 3.5)project(server)
cmake_minimum_required(VERSION 3.27)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf REQUIRED)find_package(PkgConfig REQUIRED)
pkg_search_module(GRPC REQUIRED grpc)
pkg_search_module(GRPCPP REQUIRED grpc++)find_program(_PROTOBUF_PROTOC protoc)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)add_executable(server calculator_service.cpp add.grpc.pb.cc add.grpc.pb.h add.pb.cc add.pb.h)
add_executable(client calculator_client.cpp add.grpc.pb.cc add.grpc.pb.h add.pb.cc add.pb.h)target_link_libraries(server grpc++ grpc protobuf::libprotobuf)
target_link_libraries(client grpc++ grpc protobuf::libprotobuf)
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build -j5
Python客户端
生成客户端代码
pip3 install grpcio-tools
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. add.proto
调用客户端代码
import grpc
import add_pb2
import add_pb2_grpcdef run():channel = grpc.insecure_channel('localhost:50052') # 假设服务器运行在本地的 50051 端口stub = add_pb2_grpc.CalculatorStub(channel)# 构造请求request = add_pb2.AddRequest(operand1=10, operand2=20)# 调用远程函数response = stub.Add(request)print("Sum:", response.result)if __name__ == '__main__':run()
1.Q&A
- 如果我会使用rpc,能给我带来什么好处呢?
使用rpc可以屏蔽掉底层传输层的协议,只关注我们需要调用的函数接口,而且grpc性能很好。grpc是跨语言的工具,意思是客户端可以是Python实现,而服务端可以是C++实现。 - 自己实现一个rpc库需要考虑哪些方面?
序列化协议选择,服务发现和注册,负载均衡,跨语言,性能。
序列化协议:例如 Protocol Buffers、MessagePack、JSON 等
2.学习版rpc
- button_rpc
- tinyrpc
- mini-tinyrpc
3.gRPC压缩算法
gRPC 支持多种压缩算法,开发者可以根据应用需求选择适当的算法。以下是 gRPC 支持的主要压缩算法:
- gzip: gRPC 默认使用的压缩算法。它是一种通用的压缩算法,具有较高的压缩比和广泛的支持。
- identity: 这是一种无压缩算法,即不进行压缩。如果你希望在 gRPC 中禁用压缩,可以选择使用 “identity”。
- deflate: gRPC 支持使用 deflate 算法进行压缩。这是一种流行的压缩算法,类似于 gzip,但在某些情况下可能表现不同。
参考
- 从零开始:protobuf原理与实战代码详解
- protobuf code
- Protocol Buffer Compiler Installation
- 从零开始学习gRPC:实现高性能跨语言微服务【C++和Python】
- Protobuf和gRpc快速实践
相关文章:
【C++版本】protobuf与gRPC
文章目录 一、Protobuf二、安装以及使用protoc三、gRPC1.Q&A2.学习版rpc3.gRPC压缩算法 参考 一、Protobuf Google Protocol Buffers(protobuf)是一种语言中立、平台中立的序列化协议,旨在高效地将结构化数据进行序列化和反序列化。它主要…...
要抓住国际白银现货行情 以下这几点需要注意
国际白银现货行情最近表现不甚稳定,在七月上旬出现了比较强势的上涨,但随后出现强势的下跌,跌破了30关口。如果我们要抓住国际白银现货行情,那么以下这几点我们就需要注意。 一,建立交易计划,并且按计划执行…...
【计算机毕业设计】720图书馆智能选座系统
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
java面向对象重点总结
文章目录 java面向对象重点总结类与实例构造方法方法重载属性与修饰符封装继承多态重构抽象类接口抽象类和接口的区别:集合泛型 java面向对象重点总结 对象是一个自包含的实体,用一组可识别的特性和行为来标识。 面向对象编程,英文叫Object…...
1321:【例6.3】删数问题(Noip1994)
大模拟 #include<bits/stdc.h> using namespace std; int s,len; char c[245]; int main(){cin>>c>>s;//读入高精度数和待删除的数lenstrlen(c);//1、寻找第一个下降序列的转折点,删去//2、如果找不到,意味着全部递增,删…...
使用 Python 中的 ELSER 进行Serverless 语义搜索:探索夏季奥运会历史
作者:来自 Elastic Essodjolo Kahanam 本博客介绍如何使用语义搜索以自然语言表达形式从 Elasticsearch 索引中获取信息。我们将创建一个无服务器 Elasticsearch 项目,将之前的奥运会数据集加载到索引中,使用推理处理器和 ELSER 模型生成推理…...
[HITCON 2017]SSRFme 1
目录 代码审计 符号shell_exec() 函数:GET " . escapeshellarg($_GET["url"]):pathinfo($_GET["filename"]basename() 题目解析 代码审计 118.182.186.90 <?phpif (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {$http_x_headers explod…...
看不见的硝烟:中国网络安全三十年沉浮史
2022 年 5 月 16 日,俄罗斯黑客组织 KillNet 向包括美国、英国、德国在内 10 个国家的政府正式 “宣战”。 2022 年 4 月 28 日,一则消息刷屏,北京健康宝在使用高峰期间,遭受到境外网络攻击。北京健康宝保障团队进行了及时有效应…...
3.7.物体检测算法
物体检测算法 1.R-CNN 首先使用启发式搜索算法来选择锚框,使用预训练模型对每个锚框抽取特征,训练一个SVM来对类别分类,最后训练一个线性回归模型来预测边缘框偏移。 R-CNN比较早,所以使用的是SVM 1.1 兴趣区域(RoI)池化…...
Spring源码解析(27)之AOP的核心对象创建过程2
一、前言 我们在上一节中已经介绍了Advisor的创建过程,当时我们创建的logUtil这bean,他在 resolveBeforeInstantiation返回的是null,那么就会继续往下执行doCreateBean方法。 二、源码分析 protected Object doCreateBean(String beanName,…...
【题解】【数学】—— [CSP-J 2023] 小苹果
【题解】【数学】—— [CSP-J 2023] 小苹果 [CSP-J 2023] 小苹果题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 1.题意分析2.代码 [CSP-J 2023] 小苹果 前置知识:数学分组思想,整体思想。 [CSP-J 2023] 小苹果 题目描述 小 Y 的桌子上…...
python实现微信聊天图片DAT文件还原
完整代码如下: from glob import glob import os from tqdm import tqdmdef get_sign(dat_r):signatures [(0x89, 0x50, 0x4e), (0x47, 0x49, 0x46), (0xff, 0xd8, 0xff)]mats [".png", ".gif", ".jpg"]for now in dat_r:for j, x…...
栈与队列——1.有效的括号
力扣题目链接 给定一个只包括 (,),{,},[,] 的字符串,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效…...
C语言家教记录(二)
C语言家教记录(二) 导语输入输出表达式算数运算符示例程序赋值运算符简单赋值复合赋值 总结和复习 导语 本次授课内容如下:输入输出、表达式 有时间则讲解选择语句 辅助教材为 《C语言程序设计现代方法(第2版)》 输…...
Cocos Creator2D游戏开发(10)-飞机大战(8)-计分和结束
现在游戏基本能完了, 飞机能发射子弹,打了敌机,敌机也能炸; 接下来要做计分了; 步骤: 搞出一个lable让lable显示炸了多少飞机 开搞: ①创建一个Lable标签 ② root.ts文件 添加 property(Label) player_score: Label; // 标签属性 标签绑定 ③ 代码添加 注册 然后回调 contac…...
经验分享:大数据多头借贷风险对自身的不利影响?
在现代金融体系中,大数据技术的应用使得多头借贷成为一种普遍现象。多头借贷指的是个人或企业在短时间内同时或近期内申请多笔贷款或信用产品,这种行为可能带来一系列财务和信用风险。以下是大数据多头借贷风险对个人自身可能产生的不利影响:…...
OpenCV 图像处理 轮廓检测基本原理
文章目录 基本原理关键函数和参数注意事项 示例代码示例效果代码详解findContours 函数原型findContours函数变体 基本原理 轮廓发现是图像处理中的一个重要步骤,用于检测物体的边界和形状。 图像预处理: 轮廓发现通常在灰度图像上进行。因此࿰…...
C 语言动态顺序表
test.h #ifndef _TEST_H #define _TEST_H #include <stdio.h> #include <stdlib.h> #include <string.h>typedef int data_type;// 定义顺序表结构体 typedef struct List{data_type *data; // 顺序表数据int size; // 顺序表当前长度int count; // 顺序表容…...
擅于辩论的人可以将黑的说成白的,但是存在无法解决的矛盾
擅于辩论的人有能力通过逻辑、证据和修辞等手段,巧妙地引导听众接受与事实相反的观点。 然而,这并不意味着擅于辩论的人就能将任何事物都颠倒黑白。辩论的基础是事实和逻辑,即使是最优秀的辩手,也必须遵循这些基本原则。如果某个…...
java的命令执行漏洞揭秘
0x01 前言 在Java中可用于执行系统命令常见的方式有两种,API为:java.lang.Runtime、java.lang.ProcessBuilder 0x02 java.lang.Runtime GetMapping("/runtime/exec")public String CommandExec(String cmd) {Runtime run Runtime.getRunti…...
告别PCtoLCD2002!这款单片机调试助手如何用3步搞定OLED汉字显示?
3步解锁OLED汉字显示:新一代嵌入式开发神器实战指南 在嵌入式开发领域,OLED屏幕的汉字显示一直是让开发者头疼的难题。传统方案如PCtoLCD2002等取模软件不仅操作繁琐,生成的代码还需要大量手工调整。如今,一款名为单片机多功能调试…...
海康WEBSDK无插件版实战:零基础构建WEB端网络摄像机实时监控系统
1. 环境准备:5分钟搞定基础配置 第一次接触海康WEBSDK无插件版时,我也被那些专业术语吓到过。但实际操作后发现,只要准备好三样东西就能开工:一台能联网的电脑、海康网络摄像机、以及从官网下载的开发包。这里分享几个新手容易踩的…...
告别卡顿!用UniApp的RenderJS为你的APP手势和动画性能提速(实战解析)
告别卡顿!用UniApp的RenderJS为你的APP手势和动画性能提速(实战解析) 在移动应用开发中,流畅的用户体验往往决定了产品的成败。当你在UniApp框架下开发APP时,是否遇到过这样的场景:地图拖拽时出现明显延迟&…...
BGE嵌入模型实战手册:面向开发者的检索增强解决方案
BGE嵌入模型实战手册:面向开发者的检索增强解决方案 【免费下载链接】FlagEmbedding Dense Retrieval and Retrieval-augmented LLMs 项目地址: https://gitcode.com/GitHub_Trending/fl/FlagEmbedding 在构建智能问答系统时,你是否曾遇到这些挑战…...
双摆控制系统:LQR、LQG、LQI控制器及龙伯格观测器文件清单
移动小车上双摆的LQR、LQG、LQI控制器和龙伯格观测器文件列表: LQG.m LQG_non_linear.m LQI.m LQR.m LQR_Non_linear.m Luenberger_observer.m Observer_non_linear.m 最近蹲在实验室的工位上啃移动小车双摆的控制代码,翻来覆去调了快两周,终…...
从浮点到定点:手把手教你用MATLAB自定义函数实现加减乘除(避坑溢出与精度损失)
从浮点到定点:手把手教你用MATLAB自定义函数实现加减乘除(避坑溢出与精度损失) 当算法需要从实验室环境迁移到嵌入式设备时,浮点运算的硬件开销常常成为瓶颈。这时定点数运算就像一把手术刀——精准控制每个比特的用途,…...
数字电路设计避坑指南:RS触发器和JK触发器的常见应用误区与波形分析
数字电路设计避坑指南:RS触发器和JK触发器的常见应用误区与波形分析 在数字电路设计中,触发器作为时序逻辑的基础单元,其稳定性和可靠性直接影响整个系统的性能。RS触发器和JK触发器作为两种最常用的触发器类型,看似简单的逻辑背…...
从新手到专家:OpenCore配置工具OCAT的实战应用指南
从新手到专家:OpenCore配置工具OCAT的实战应用指南 【免费下载链接】OCAuxiliaryTools Cross-platform GUI management tools for OpenCore(OCAT) 项目地址: https://gitcode.com/gh_mirrors/oc/OCAuxiliaryTools 如果你正在为黑苹果系…...
毕业论文神器 2026 降AI率平台推荐:工具对比+最好用AI推荐
2026年真正好用的AI论文降重与改写工具,核心看降重效果、去AI味、格式保留、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 …...
保姆级教程:CLIP-GmP-ViT-L-14图文匹配工具一键部署,小白也能玩转AI识图
保姆级教程:CLIP-GmP-ViT-L-14图文匹配工具一键部署,小白也能玩转AI识图 你是不是经常好奇,AI到底是怎么看懂图片的?给它一张照片和几个文字描述,它怎么知道哪个描述最贴切?今天,我就带你亲手搭…...
