【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…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
