十八、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…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...