【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…...

爬虫中常见的加密算法Base64伪加密,MD5加密【DES/AES/RSA/SHA/HMAC】及其代码实现(一)
目录 基础常识 Base64伪加密 python代码实现 摘要算法 1. MD5 1.1 JavaScript 实现 1.2 Python 实现 2. SHA 2.1 JavaScript 实现 2.2 Python 实现 2.3 sha系列特征 3. HMAC 3.1 JavaScript 实现 3.2 Python 实现 对称加密 一. 常见算法归纳 1. 工作模式归纳 …...

C语言数据在内存中的存储超详解
文章目录 1. 整数在内存中的存储2. 大小端字节序和字节序判断2. 1 什么是大小端?2. 2 为什么会有大小端?2. 3 练习 3. 浮点数在内存中的存储3. 1 一个代码3. 2 浮点数的存储3. 2. 1 浮点数存的过程3. 2. 2 浮点数取的过程3. 3 题目解析 1. 整数在内存中的…...

【大模型】【NL2SQL】基本原理
三个输入: prompt 用户输入 数据库表格等信息 sql 语句...

RK3568平台(显示篇)DRM vop驱动程序分析
一.设备树配置 vopb: vopff900000 {compatible "rockchip,rk3399-vop-big";reg <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>;interrupts <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;assigned-clocks <&cru ACLK_VOP0>, &…...

vue3 动态加载组件
//模版调用 <component :is"geticon(item.icon)" />//引入 import { ref, onMounted, markRaw, defineAsyncComponent } from vue;//异步添加icon图标组建 function geticon(params) {const modules import.meta.glob(../components/icons/*.vue);const link …...

Latex on overleaf入门语法
Latex on overleaf入门语法 前言基本结构序言 简单的格式化命令添加注释:%加粗、斜体、下划线有序列表、无序列表 添加图片图片的标题、标签和引用 添加表格一个简单的表格为表格添加边框标题、标签、引用 数学表达式基本的数学命令 基本格式摘要段落、新行章节、分…...

使用Echarts来实现数据可视化
目录 一.什么是ECharts? 二.如何使用Springboot来从后端给Echarts返回响应的数据? eg:折线图: ①Controller层: ②service层: 一.什么是ECharts? ECharts是一款基于JavaScript的数据可视化图标库,提供直观&…...

一文搞懂GIT
文章目录 1. GiT概述1.1 GIT概述1.2 GIT安装 2. GIT组成3. GIT基本命令3.1 基本命令3.2 分支操作3.3 远程操作3.4 标签操作3.5 其他命令 1. GiT概述 1.1 GIT概述 Git 是一个分布式版本控制系统,被广泛应用于软件开发中。 Git 具有众多优点,比如&#…...

jQuery入门(四)案例
jQuery 操作入门案例 一、复选框案例 功能: 列表的全选,反选,全不选功能实现。 实现步骤和分析: - 全选 1. 为全选按钮绑定单击事件。 2. 获取所有的商品项复选框元素,为其添加 checked 属性,属性值为 true。 -…...

揭秘MITM攻击:原理、手法与防范措施
中间人攻击发生时,攻击者会在通讯两端之间插入自己,成为通信链路的一部分。攻击者可以拦截、查看、修改甚至重新定向受害者之间的通信数据,而不被双方察觉。这种攻击常见于未加密的Wi-Fi网络、不安全的HTTP连接或者通过社会工程学手段诱导受害…...