十八、Rust gRPC 多 proto 演示
十八、Rust gRPC 多 proto 演示
网上及各官方资料,基本是一个 proto 文件,而实际项目,大多是有层级结构的多 proto 文件形式,本篇文章 基于此诉求,构建一个使用多 proto 文件的 rust grpc 使用示例。
关于 grpc 的实现,找到两个库:
-
Tonic:https://github.com/hyperium/tonic,8.9k Star、852 Commits、2024-03-12 updated。
-
PingCAP 的 grpc-rs:https://github.com/tikv/grpc-rs,1.8k Star、357 Commits、2023-08 updated。
据说 PingCAP 的 grpc-rs benchmark 稍高一些,但看关注度和提交量不如 tonic,且据说 tonic 开发体验更好一些,本篇以 tonic 为例。
编译 Protobuf,还需要 protoc,可以参考官方文档,这里先给出 macOS 的:
brew install protobuf- https://grpc.io/docs/protoc-installation/
关于 Tonic:Tonic 是基于 HTTP/2 的 gRPC 实现,专注于高性能,互通性和灵活性;
目录说明
.
├── Cargo.toml
├── README.md
├── build.rs
├── proto
│ ├── basic
│ │ └── basic.proto
│ ├── goodbye.proto
│ └── hello.proto
└── src├── bin│ ├── client.rs│ └── server.rs├── lib.rs└── proto-gen├── basic.rs├── goodbye.rs└── hello.rs
build.rs存放通过 proto 生成 rs 的脚本;proto目录放置 grpc 的 proto 文件,定义服务和消息体;src常规意义上的项目源码目录;proto-gen目录存放build.rs编译 proto 后生成的 rs 文件;lib.rs引入 proto 的 rs 文件;bin目录下进行 proto 所定义服务的实现,此例为 客户端、服务端 的实现;
创建项目
- 创建一个 lib 项目:
cargo new grpc --lib
- Cargo.toml
[package]
name = "grpc"
version = "0.1.0"
edition = "2021"
description = "A demo to learn grpc with tonic."# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[[bin]]
name="grpc_server"
path="src/bin/server.rs"[[bin]]
name="grpc_client"
path="src/bin/client.rs"[dependencies]
prost = "0.12.3"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
tonic = "0.11.0"[build-dependencies]
tonic-build = "0.11.0"
定义服务
- grpc/proto/basic/basic.proto
syntax = "proto3";package basic;message BaseResponse {string message = 1;int32 code = 2;
}
- grpc/proto/hello.proto
syntax = "proto3";import "basic/basic.proto";package hello;service Hello {rpc Hello(HelloRequest) returns (HelloResponse) {}
}message HelloRequest {string name = 1;
}message HelloResponse {string data = 1;basic.BaseResponse message = 2;
}
- grpc/proto/goodbye.proto
syntax = "proto3";import "basic/basic.proto";package goodbye;service Goodbye {rpc Goodbye(GoodbyeRequest) returns (GoodbyeResponse) {}
}message GoodbyeRequest {string name = 1;
}message GoodbyeResponse {string data = 1;basic.BaseResponse message = 2;
}
配置编译
Rust 约定:在 build.rs 中定义的代码,会在编译真正项目代码前被执行,因此,可以在这里先编译 protobuf 文件;
- grpc/Cargo.toml 引入
[build-dependencies]
tonic-build = "0.11.0"
- grpc/build.rs
use std::error::Error;
use std::fs;static OUT_DIR: &str = "src/proto-gen";fn main() -> Result<(), Box<dyn Error>> {let protos = ["proto/basic/basic.proto","proto/hello.proto","proto/goodbye.proto",];fs::create_dir_all(OUT_DIR).unwrap();tonic_build::configure().build_server(true).out_dir(OUT_DIR).compile(&protos, &["proto/"])?;rerun(&protos);Ok(())
}fn rerun(proto_files: &[&str]) {for proto_file in proto_files {println!("cargo:rerun-if-changed={}", proto_file);}
}
稍作解释:
OUT_DIR全局定义 proto 文件编译后的输出位置(默认在target/build目录下)。let protos = [...]声明了所有待编译 proto 文件。tonic_build::configure().build_server(true)是否编译 server 端,项目以 proto 为基准,则编就完了。.compile(&protos, &["proto/"])?;开始编译。
最终生成:
- grpc/src/proto-gen/
- basic.rs、hello.rs、goodbye.rs
由 proto 生成的原代码,内容一般较长,这里不贴出,感兴趣的读者,运行一下就可以看到。另外翻看其代码,可以看到:
- 为客户端生成的
HelloClient类型:impl<T> HelloClient<T>实现了Clone、Sync及Send,因此可以跨线程使用。 - 为服务端生成的
HelloServer类型:impl<T: Hello> HelloServer<T> {}包含了impl<T: Hello>,预示着我们创建HelloServer实现,假设为HelloService时,需实现该Hello Trait。
引入proto生成的文件
- grpc/src/lib.rs
#![allow(clippy::derive_partial_eq_without_eq)]pub mod basic {include!("./proto-gen/basic.rs");
}pub mod hello {include!("./proto-gen/hello.rs");
}pub mod goodbye {include!("./proto-gen/goodbye.rs");
}
-
这里使用了标准库提供的
include!来引入源文件; -
如果没有定义 proto 编译输出位置的话,默认是在
target/build目录下,此时需要使用 tonic 提供的include_proto!("hello")宏,来引入对应文件,而不用额外提供路径了,其中的hello为 grpc 的 “包名”(proto文件中的 “package xxx;”),具体来说就是:- 注释掉
grpc/build.rs中.out_dir(OUT_DIR)一行。 grpc/src/lib.rs中:include!("./proto-gen/basic.rs");改为include_proto!("basic");。include!("./proto-gen/hello.rs");改为include_proto!("hello");。include!("./proto-gen/goodbye.rs");改为include_proto!("goodbye");。
- 但这样,在进行 server、client 实现、源码编写时,将无法正常引用,致使大量 “漂红” (只 IDE 下这样,如 CLion,不影响 shell 下编译及运行) 。
- 注释掉
-
参考官方文档:https://docs.rs/tonic/latest/tonic/macro.include_proto.html
服务实现
服务端实现各语言基本类似,为对应 proto 定义,创建相应的 Service 实现即可:
- grpc/src/bin/server.rs
use tonic::{Request, Response, Status};
use tonic::transport::Server;use grpc::basic::BaseResponse;
use grpc::goodbye::{GoodbyeRequest, GoodbyeResponse};
use grpc::goodbye::goodbye_server::{Goodbye, GoodbyeServer};
use grpc::hello;
use hello::{HelloRequest, HelloResponse};
use hello::hello_server::{Hello, HelloServer};#[derive(Default)]
pub struct HelloService {}#[tonic::async_trait]
impl Hello for HelloService {async fn hello(&self, req: Request<HelloRequest>) -> Result<Response<HelloResponse>, Status> {println!("hello receive request: {:?}", req);let response = HelloResponse {data: format!("Hello, {}", req.into_inner().name),message: Some(BaseResponse {message: "Ok".to_string(),code: 200,}),};Ok(Response::new(response))}
}#[derive(Default)]
pub struct GoodbyeService {}#[tonic::async_trait]
impl Goodbye for GoodbyeService {async fn goodbye(&self,req: Request<GoodbyeRequest>,) -> Result<Response<GoodbyeResponse>, Status> {println!("goodbye receive request: {:?}", req);let response = GoodbyeResponse {data: format!("Goodbye, {}", req.into_inner().name),message: Some(BaseResponse {message: "Ok".to_string(),code: 200,}),};Ok(Response::new(response))}
}#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {let addr = "0.0.0.0:50051".parse()?;println!("server starting at: {}", addr);Server::builder().add_service(HelloServer::new(HelloService::default())).add_service(GoodbyeServer::new(GoodbyeService::default())).serve(addr).await?;Ok(())
}
- grpc/src/bin/client.rs
use tonic::Request;
use tonic::transport::Endpoint;use grpc::goodbye::goodbye_client::GoodbyeClient;
use grpc::goodbye::GoodbyeRequest;
use grpc::hello;
use hello::hello_client::HelloClient;
use hello::HelloRequest;#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {let addr = Endpoint::from_static("https://127.0.0.1:50051");let mut hello_cli = HelloClient::connect(addr.clone()).await?;let request = Request::new(HelloRequest {name: "tonic".to_string(),});let response = hello_cli.hello(request).await?;println!("hello response: {:?}", response.into_inner());let mut goodbye_cli = GoodbyeClient::connect(addr).await?;let request = Request::new(GoodbyeRequest {name: "tonic".to_string(),});let response = goodbye_cli.goodbye(request).await?;println!("goodbye response: {:?}", response.into_inner());Ok(())
}
运行及测试
cargo run --bin grpc_server
cargo run --bin grpc_client
故障时重新编译:
cargo clean && cargo build
关于 Github Action
- 需添加步骤
- name: Install protocrun: sudo apt-get install -y protobuf-compiler
完事 ~~
参考资料:
-
Rust grpc 实现 - https://jasonkayzk.github.io/2022/12/03/Rust%E7%9A%84GRPC%E5%AE%9E%E7%8E%B0Tonic/
-
Tonic 流式 grpc - https://github.com/hyperium/tonic/blob/master/examples/routeguide-tutorial.md
-
开源库 - https://github.com/tokio-rs/prost
-
Tonic - https://github.com/hyperium/tonic
相关文章:
十八、Rust gRPC 多 proto 演示
十八、Rust gRPC 多 proto 演示 网上及各官方资料,基本是一个 proto 文件,而实际项目,大多是有层级结构的多 proto 文件形式,本篇文章 基于此诉求,构建一个使用多 proto 文件的 rust grpc 使用示例。 关于 grpc 的实现…...
【Linux】Linux64位环境下编译32位报错skipping incompatible的解决办法
本文首发于 ❄️慕雪的寒舍 问题 如题,当我尝试在wsl2的ubuntu中使用-m32选项编译32位程序的时候,出现了下面的两种报错 ❯ g -m32 test.cpp -o test1 && ./test1 In file included from test.cpp:1: /usr/include/stdio.h:27:10: fatal error…...
vue指令v-model
<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>vue指令v-model</title> </head>…...
CentOS安装MySQL数据库
一、更新yum源 #下载对应repo文件 wget -O CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo #清除缓存 yum clean all #生成新缓存 yum makecache #更新 yum update -y 二、安装MySQL #获取源 wget http://repo.mysql.com/mysql80-community-release-el7-3.…...
从B2B转向B2B2C模式:工业品牌史丹利百得的转型历程
图片来源:Twitter 在当今数据驱动的营销环境中,企业努力更好了解客户,并在整个客户旅程中提供个性化体验。史丹利百得(Stanley Black & Decker)是一家领先的工具和工业设备供应商,近年来开始重大转型。…...
Redis群集模式和rsync远程同步
一、Redis群集模式 1.1 概念 1.2 作用 1.2.1 Redis集群的数据分片 1.2.2 Redis集群的主从复制模型 1.3 搭建Redis 群集模式 1.3.1 开启群集功能 1.3.2 启动redis节点 1.3.3 启动集群 1.3.4 测试群集 二、rsync远程同步 2.1 概念 2.2 同步方式 2.3 备份的方式 2.4…...
JAVA—抽象—定义抽象类Converter及其子类WeightConverter
同样,我们由这道题引出抽象类,抽象方法这个概念。 按下面要求定义类Converter及其子类WeightConverter 定义抽象类:Converter: 定义一个抽象类Converter,表示换算器,其定义的如下: 一个私有…...
面对复杂多变的网络攻击,企业应如何守护网络安全
企业上云,即越来越多的企业把业务和数据,迁移到云端。随着云计算、大数据、物联网、人工智能等技术的发展,用户、应用程序和数据无处不在,企业之间的业务边界逐渐被打破,网络攻击愈演愈烈,手段更为多。 当前…...
计算机网络练习-计算机网络概述与性能指标
计算机网络概述 ----------------------------------------------------------------------------------------------------------------------------- 1. 计算机网络最据本的功能的是( )。 1,差错控制 Ⅱ.路由选择 Ⅲ,分布式处理 IV.传输控制 …...
vite vue3 ts import.meta在vscode中报错
问题描述:开发使用的框架为vitevue3ts,在开发过程中莫名其妙报仅当“--module”选项为“es2020”、“esnext”或“系统”时才允许使用“import.meta”元属性 问题解决: 通过更改tsconfig.json的module为esnext,es2022等࿰…...
Java synchronized(详细)
synchronized 一,介绍 在Java中,synchronized关键字用于解决多线程并发访问共享资源时可能出现的线程安全问题。当多个线程同时访问共享资源时,如果没有合适的同步机制,可能会导致以下问题: 竞态条件(…...
算法设计与分析实验报告python实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)
一、 实验目的 1.加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握; 2.提高学生利用课堂所学知识解决实际问题的能力; 3.提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…...
实训问题总结——ajax用get可以成功调用controller方法,用POST就出404错误
因为传输密码时必须用POST。 还有用GET传输参数,说有非法字符,想试试POST是否可以解决。 404错误的三个大致原因,1:找不到对的请求路径,2:请求方式错误,3、请求参数错误。 后来可以调用了。但…...
1、认识MySQL存储引擎吗?
目录 1、MySQL存储引擎有哪些? 2、默认的存储引擎是哪个? 3、InnoDB和MyISAM有什么区别吗? 3.1、关于事务 3.2、关于行级锁 3.3、关于外键支持 3.4、关于是否支持MVCC 3.5、关于数据安全恢复 3.6、关于索引 3.7、关于性能 4、如何…...
微信小程序媒体查询
在微信小程序中,media媒体查询不支持screen关键字,因为小程序页面是再webview中渲染的,而不是在浏览器中渲染的。 在设置样式时,可以使用 wxss 文件中的 media 规则来根据屏幕宽度或高度设置不同的样式。 device-width:设备屏幕…...
前端(动态雪景背景+动态蝴蝶)
1.CSS样式 <style>html, body, a, div, span, table, tr, td, strong, ul, ol, li, h1, h2, h3, p, input {font-weight: inherit;font-size: inherit;list-style: none;border-spacing: 0;border: 0;border-collapse: collapse;text-decoration: none;padding: 0;margi…...
软考-系统集成项目管理中级-新一代信息技术
本章历年考题分值统计 本章重点常考知识点汇总清单(掌握部分可直接理解记忆) 本章历年考题及答案解析 32、2019 年上半年第 23 题 云计算通过网络提供可动态伸缩的廉价计算能力,(23)不属于云计算的特点。 A.虚拟化 B.高可扩展性 C.按需服务 D.优化本地存储 【参考…...
【卷积神经网络进展】
打基础日常记录 CNN基础知识1. 感知机2. DNN 深度神经网络(全连接神经网络)DNN 与感知机的区别DNN特点,全连接神经网络DNN前向传播和反向传播 3. CNN结构【提取特征分类】4. CNN应用于文本 CNN基础知识 1. 感知机 单层感知机就是一个二分类…...
yarn的安装和使用
windows mac 环境 yarn的安装和使用 yarn安装 npm install -g yarnyarn设置代理 yarn config set registry https://registry.npm.taobao.org -gyarn官方源 yarn config set registry https://registry.yarnpkg.comyarn使用 // 查看板本 yarn --version// 安装指定包 yarn…...
Golang | Leetcode Golang题解之第10题正则表达式匹配
题目: 题解: func isMatch(s string, p string) bool {m, n : len(s), len(p)matches : func(i, j int) bool {if i 0 {return false}if p[j-1] . {return true}return s[i-1] p[j-1]}f : make([][]bool, m 1)for i : 0; i < len(f); i {f[i] m…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
