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

【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

  1. 如果我会使用rpc,能给我带来什么好处呢?
    使用rpc可以屏蔽掉底层传输层的协议,只关注我们需要调用的函数接口,而且grpc性能很好。grpc是跨语言的工具,意思是客户端可以是Python实现,而服务端可以是C++实现。
  2. 自己实现一个rpc库需要考虑哪些方面?
    序列化协议选择,服务发现和注册,负载均衡,跨语言,性能。
    序列化协议:例如 Protocol Buffers、MessagePack、JSON 等

2.学习版rpc

  • button_rpc
  • tinyrpc
  • mini-tinyrpc

3.gRPC压缩算法

gRPC 支持多种压缩算法,开发者可以根据应用需求选择适当的算法。以下是 gRPC 支持的主要压缩算法:

  1. gzip: gRPC 默认使用的压缩算法。它是一种通用的压缩算法,具有较高的压缩比和广泛的支持。
  2. identity: 这是一种无压缩算法,即不进行压缩。如果你希望在 gRPC 中禁用压缩,可以选择使用 “identity”。
  3. 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&#xff08;protobuf&#xff09;是一种语言中立、平台中立的序列化协议&#xff0c;旨在高效地将结构化数据进行序列化和反序列化。它主要…...

要抓住国际白银现货行情 以下这几点需要注意

国际白银现货行情最近表现不甚稳定&#xff0c;在七月上旬出现了比较强势的上涨&#xff0c;但随后出现强势的下跌&#xff0c;跌破了30关口。如果我们要抓住国际白银现货行情&#xff0c;那么以下这几点我们就需要注意。 一&#xff0c;建立交易计划&#xff0c;并且按计划执行…...

【计算机毕业设计】​720图书馆智能选座系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

java面向对象重点总结

文章目录 java面向对象重点总结类与实例构造方法方法重载属性与修饰符封装继承多态重构抽象类接口抽象类和接口的区别&#xff1a;集合泛型 java面向对象重点总结 对象是一个自包含的实体&#xff0c;用一组可识别的特性和行为来标识。 面向对象编程&#xff0c;英文叫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、寻找第一个下降序列的转折点&#xff0c;删去//2、如果找不到&#xff0c;意味着全部递增&#xff0c;删…...

使用 Python 中的 ELSER 进行Serverless 语义搜索:探索夏季奥运会历史

作者&#xff1a;来自 Elastic Essodjolo Kahanam 本博客介绍如何使用语义搜索以自然语言表达形式从 Elasticsearch 索引中获取信息。我们将创建一个无服务器 Elasticsearch 项目&#xff0c;将之前的奥运会数据集加载到索引中&#xff0c;使用推理处理器和 ELSER 模型生成推理…...

[HITCON 2017]SSRFme 1

目录 代码审计 符号shell_exec() 函数:GET " . escapeshellarg($_GET["url"])&#xff1a;pathinfo($_GET["filename"]basename() 题目解析 代码审计 118.182.186.90 <?phpif (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {$http_x_headers explod…...

看不见的硝烟:中国网络安全三十年沉浮史

2022 年 5 月 16 日&#xff0c;俄罗斯黑客组织 KillNet 向包括美国、英国、德国在内 10 个国家的政府正式 “宣战”。 2022 年 4 月 28 日&#xff0c;一则消息刷屏&#xff0c;北京健康宝在使用高峰期间&#xff0c;遭受到境外网络攻击。北京健康宝保障团队进行了及时有效应…...

3.7.物体检测算法

物体检测算法 1.R-CNN ​ 首先使用启发式搜索算法来选择锚框&#xff0c;使用预训练模型对每个锚框抽取特征&#xff0c;训练一个SVM来对类别分类&#xff0c;最后训练一个线性回归模型来预测边缘框偏移。 ​ R-CNN比较早&#xff0c;所以使用的是SVM 1.1 兴趣区域(RoI)池化…...

Spring源码解析(27)之AOP的核心对象创建过程2

一、前言 我们在上一节中已经介绍了Advisor的创建过程&#xff0c;当时我们创建的logUtil这bean&#xff0c;他在 resolveBeforeInstantiation返回的是null&#xff0c;那么就会继续往下执行doCreateBean方法。 二、源码分析 protected Object doCreateBean(String beanName,…...

【题解】【数学】—— [CSP-J 2023] 小苹果

【题解】【数学】—— [CSP-J 2023] 小苹果 [CSP-J 2023] 小苹果题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 1.题意分析2.代码 [CSP-J 2023] 小苹果 前置知识&#xff1a;数学分组思想&#xff0c;整体思想。 [CSP-J 2023] 小苹果 题目描述 小 Y 的桌子上…...

python实现微信聊天图片DAT文件还原

完整代码如下&#xff1a; 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.有效的括号

力扣题目链接 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效…...

C语言家教记录(二)

C语言家教记录&#xff08;二&#xff09; 导语输入输出表达式算数运算符示例程序赋值运算符简单赋值复合赋值 总结和复习 导语 本次授课内容如下&#xff1a;输入输出、表达式 有时间则讲解选择语句 辅助教材为 《C语言程序设计现代方法&#xff08;第2版&#xff09;》 输…...

Cocos Creator2D游戏开发(10)-飞机大战(8)-计分和结束

现在游戏基本能完了, 飞机能发射子弹,打了敌机,敌机也能炸; 接下来要做计分了; 步骤: 搞出一个lable让lable显示炸了多少飞机 开搞: ①创建一个Lable标签 ② root.ts文件 添加 property(Label) player_score: Label; // 标签属性 标签绑定 ③ 代码添加 注册 然后回调 contac…...

经验分享:大数据多头借贷风险对自身的不利影响?

在现代金融体系中&#xff0c;大数据技术的应用使得多头借贷成为一种普遍现象。多头借贷指的是个人或企业在短时间内同时或近期内申请多笔贷款或信用产品&#xff0c;这种行为可能带来一系列财务和信用风险。以下是大数据多头借贷风险对个人自身可能产生的不利影响&#xff1a;…...

OpenCV 图像处理 轮廓检测基本原理

文章目录 基本原理关键函数和参数注意事项 示例代码示例效果代码详解findContours 函数原型findContours函数变体 基本原理 轮廓发现是图像处理中的一个重要步骤&#xff0c;用于检测物体的边界和形状。 图像预处理&#xff1a; 轮廓发现通常在灰度图像上进行。因此&#xff0…...

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; // 顺序表容…...

擅于辩论的人可以将黑的说成白的,但是存在无法解决的矛盾

擅于辩论的人有能力通过逻辑、证据和修辞等手段&#xff0c;巧妙地引导听众接受与事实相反的观点。 然而&#xff0c;这并不意味着擅于辩论的人就能将任何事物都颠倒黑白。辩论的基础是事实和逻辑&#xff0c;即使是最优秀的辩手&#xff0c;也必须遵循这些基本原则。如果某个…...

java的命令执行漏洞揭秘

0x01 前言 在Java中可用于执行系统命令常见的方式有两种&#xff0c;API为&#xff1a;java.lang.Runtime、java.lang.ProcessBuilder 0x02 java.lang.Runtime GetMapping("/runtime/exec")public String CommandExec(String cmd) {Runtime run Runtime.getRunti…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...