- 后端
- RPC框架
【免费下载链接】grpc-rust
A native gRPC client & server implementation with async/await support.
本指南以仓库根目录 README.md 为骨架,融合 helloworld 教程、routeguide 教程 以及
tonic源码细节,系统讲解如何在 Rust 中基于异步生态搭建 gRPC 服务端与客户端。读完本文,你将掌握 gRPC 四类 RPC 的定义与编码、tonic的三大核心组件架构、特性开关与 TLS/压缩/负载均衡等生产级能力,并能独立从零写出一个可运行、可验证的 HelloWorld 示例。
项目概览:gRPC 的 Rust 原生实现
README.md 明确指出,本仓库是 gRPC 的一个 Rust 实现。gRPC 是一个高性能、开源、通用的 RPC 框架,其设计把移动端和 HTTP/2 放在首位。仓库中的核心 cratetonic是基于 HTTP/2 的 gRPC 实现,聚焦三个关键词:高性能(high performance)、互操作性(interoperability)与灵活性(flexibility)。
它被设计出来的目的有两层:
- 对
async/await提供一等公民(first class)支持; - 作为用 Rust 编写生产系统时的核心构建块(core building block)。
需要留意的是:README 中有一则重要提示——tonic的master分支当前正在准备破坏性变更(breaking changes),若想使用最新已发布版本,应关注0.14.x分支。仓库内tonic/Cargo.toml显示的版本为0.14.6,工作区级的rust-version = "1.88"即 MSRV(最低支持的 Rust 版本),与 README 中 “tonic's MSRV is1.88” 的声明一致。
三大核心组件
README 的 Overview 一节把tonic拆解为三个相互协作的组成部分:
| 组件 | 职责 | 底层技术 |
|---|---|---|
| 通用 gRPC 实现 | 提供与具体 HTTP/2 栈、编码格式解耦的通用 gRPC 语义,通过一组泛型 trait 支撑任意 HTTP/2 实现与任意编码 | tonic 自身 |
| 高性能 HTTP/2 实现 | 提供开箱即用的 Channel(客户端)与 Server(服务端) | hyper、基于稳健的tokio栈构建 |
| 代码生成(codegen) | 从protobuf定义构建客户端与服务端代码 | prost |
也就是说:tonic在最低层面允许使用任意 HTTP/2 实现配合不同类型的 gRPC 编码格式;而默认提供的transport模块则是一套基于tokio、hyper、tower的“全家桶”实现,同时被设计成一份参考实现,供需要更丰富特性的团队在此基础上继续扩展。
功能特性清单
README 列出了tonic的核心特性,这些特性也都能在 examples 中找到对应的完整示例代码:
- 双向流式传输(Bi-directional streaming)—— 客户端与服务端可同时收发消息流,对应 streaming 示例 与 routeguide 教程中的
RouteChat; - 高性能异步 IO(High performance async io)—— 基于
tokio+hyper的异步网络栈; - 互操作性(Interoperability)—— 遵循标准 gRPC 协议,可与各语言 gRPC 实现互通,interop 目录提供了互操作测试实现;
- TLS 支持,由
rustls背书—— 通过特性开关启用,示例见 tls 示例、tls_client_auth 示例 与 tls_rustls 示例; - 负载均衡(Load balancing)—— 客户端侧可配置多种均衡策略,见 load_balance 示例 与 dynamic_load_balance 示例;
- 自定义元数据(Custom metadata)—— 通过
tonic::metadata模块(metadata/key.rs、metadata/map.rs)在请求/响应中附加键值元数据; - 认证(Authentication)—— 服务端与客户端拦截器实现,见 authentication 示例;
- 健康检查(Health Checking)—— 提供标准 gRPC 健康检查服务的实现,见 tonic-health crate 与 health 示例。
此外,tonic/src/lib.rs 的 crate 级文档确认,该库的定位是“用于生产系统的核心构建块”,强调性能、互操作性与灵活性三者的平衡。
特性开关(Feature Flags):按需裁剪依赖
tonic通过 Cargo feature 精细控制启用哪些能力。tonic/src/lib.rs 与 tonic/Cargo.toml 给出了完整清单,按“默认开启 / 默认关闭”整理如下:
默认开启(default = ["router", "transport", "codegen"]):
| 特性 | 作用 |
|---|---|
transport | 启用基于hyper、tower、tokio的“全家桶”客户端与服务端实现,同时开启server与channel |
server | 仅启用transport中服务端部分(依赖h2、hyper?/server、socket2、tower?/limit、tower?/load-shed等) |
channel | 仅启用transport中客户端 Channel 部分(依赖h2、hyper?/client、hyper-timeout、tower?/balance等) |
router | 启用基于axum的服务路由 |
codegen | 启用tonic-build所需的导出与可选依赖(async-trait) |
默认关闭,按需开启:
| 特性 | 作用 |
|---|---|
tls-ring | 基于rustls的 TLS 选项,使用ringlibcrypto provider |
tls-aws-lc | 基于rustls的 TLS 选项,使用aws-lc-rslibcrypto provider(与tls-ring二选一) |
tls-native-roots | 通过rustls-native-certs为 gRPC 客户端注入系统信任根证书 |
tls-webpki-roots | 通过webpki-roots为 gRPC 客户端注入标准信任根证书 |
tls-connect-info | 为常见 TLS connector 增加Connected实现,便于在不启用其他tls-*特性时配合connect_with_connector使用自定义 TLS connector |
gzip | 启用请求、响应与流的 gzip 压缩(依赖flate2) |
deflate | 启用 deflate 压缩(依赖flate2) |
zstd | 启用 zstd 压缩(依赖zstd) |
从 tonic/Cargo.toml 的源码可以看到,tls-ring与tls-aws-lc都经由内部特性_tls-any统一接通tokio运行时与tls-connect-info,二者本质是tokio-rustls下两种 libcrypto 提供者的选择。
消息大小上限:防止内存耗尽
tonic/src/lib.rs 还披露了一个容易被忽视的默认配置:服务端与客户端都可设置最大编码/解码消息大小,以确保入站 gRPC 消息不会耗尽系统内存。默认解码上限为 4MB,编码上限为usize::MAX。生产环境中建议按业务需要显式调整。
依赖说明:关于 protoc
README 的 Dependencies 一节提醒了一个实际工程中常见的坑:tonic-build的某些 API(例如tonic_build::compile_protos())需要protoc(Protocol Buffers 编译器)来编译.proto资源文件。这意味着构建机器上需要安装 Protocol Buffers 编译器(可参考 Protocol Buffers 官方下载页获取对应平台的protoc)。与之对应,tonic-prost-build(tonic-prostcrate 的构建端,见 tonic-prost)提供了基于prost的纯 Rust 编译路径,这也是本仓库教程默认采用的方式。
快速上手:从 HelloWorld 开始
README 推荐的入门路径是 helloworld 教程(适合首次使用tonic的读者),进阶路径是 routeguide 教程(覆盖tonic全部特性)。下面按教程脉络,从零搭建一个可运行的 gRPC 服务端与客户端。
第一步:创建项目并定义 proto
$ cargo new helloworld-tonic $ cd helloworld-tonic $ mkdir proto $ touch proto/helloworld.prototonic对.proto文件存放位置没有硬性要求(教程中放在项目根目录的proto/下即可)。定义服务时,先在proto文件中声明syntax与package名——这个 package 名就是后续include_proto!宏查找生成代码的依据:
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }gRPC 共支持四类服务方法(简单 RPC、服务端流式、客户端流式、双向流式),tonic全部支持;HelloWorld 教程只用到最简单的简单 RPC,四类方法都会用到的话请看 routeguide 教程(仓库中的完整定义见 examples/proto/routeguide/route_guide.proto)。
第二步:配置 Cargo.toml 与 build.rs
examples/helloworld-tutorial.md 给出了完整依赖清单。核心要点:
[[bin]] # Bin to run the HelloWorld gRPC server name = "helloworld-server" path = "src/server.rs" [[bin]] # Bin to run the HelloWorld gRPC client name = "helloworld-client" path = "src/client.rs" [dependencies] tonic = "*" prost = "0.14" tonic-prost = "*" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } [build-dependencies] tonic-prost-build = "*"然后在项目根目录(不是src下)创建build.rs,把 proto 编译接入 cargo 构建流程:
fn main() -> Result<(), Box<dyn std::error::Error>> { tonic_prost_build::compile_protos("proto/helloworld.proto")?; Ok(()) }routeguide 教程中用的是带unwrap_or_else的错误处理变体,效果等价。这样每次cargo build都会自动保持生成代码与.proto定义同步,无需额外步骤。
第三步:编写服务端
服务端实现分两步:把生成代码引入作用域,然后实现生成的服务 trait。首先生成代码通过include_proto!宏引入(参数是 proto 里的package 名,不是文件名):
use tonic::{transport::Server, Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; pub mod hello_world { tonic::include_proto!("helloworld"); }接着实现Greetertrait。注意#[tonic::async_trait]属性宏让 trait 支持 async 方法(其内部基于async-trait,对应tonic的codegen特性):
#[derive(Debug, Default)] pub struct MyGreeter {} #[tonic::async_trait] impl Greeter for MyGreeter { async fn say_hello( &self, request: Request<HelloRequest>, ) -> Result<Response<HelloReply>, Status> { println!("Got a request: {:?}", request); let reply = HelloReply { message: format!("Hello {}!", request.into_inner().name), }; Ok(Response::new(reply)) } }注意:gRPC 请求与响应的字段是私有的,必须通过
request.into_inner()解包后才能访问name等字段;同理构造响应时要Response::new(...)包装。
最后在tokio运行时上启动服务:
#[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let addr = "[::1]:50051".parse()?; let greeter = MyGreeter::default(); Server::builder() .add_service(GreeterServer::new(greeter)) .serve(addr) .await?; Ok(()) }运行cargo run --bin helloworld-server即可启动服务。验证方式有两种:使用支持 gRPC 的 GUI 客户端(如 Postman),或使用grpcurl:
$ grpcurl -plaintext -import-path ./proto -proto helloworld.proto -d '{"name": "Tonic"}' '[::1]:50051' helloworld.Greeter/SayHello预期响应:
{ "message": "Hello Tonic!" }第四步:编写客户端
客户端同样通过include_proto!引入生成代码,然后connect到服务端地址发起调用。GreeterClient::connect返回的客户端是mut 的,因为它需要维护内部状态:
use hello_world::greeter_client::GreeterClient; use hello_world::HelloRequest; pub mod hello_world { tonic::include_proto!("helloworld"); } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let mut client = GreeterClient::connect("http://[::1]:50051").await?; let request = tonic::Request::new(HelloRequest { name: "Tonic".into(), }); let response = client.say_hello(request).await?; println!("RESPONSE={:?}", response); Ok(()) }分别在两个终端运行cargo run --bin helloworld-server与cargo run --bin helloworld-client,服务端终端会打印出收到的请求,客户端终端会打印出响应——一个最小可运行的 gRPC 闭环就完成了。
深入:四类 RPC 与流式处理模式
routeguide 教程 用一个“路线导航”应用完整演示了 gRPC 的四类服务方法,其定义方式如下:
// 简单 RPC:客户端发一个请求,等一个响应 rpc GetFeature(Point) returns (Feature) {} // 服务端流式:`stream` 放在响应类型前 rpc ListFeatures(Rectangle) returns (stream Feature) {} // 客户端流式:`stream` 放在请求类型前 rpc RecordRoute(stream Point) returns (RouteSummary) {} // 双向流式:请求与响应前都放 `stream` rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}在实现层面,四类方法对应的tonic::Request<T>/ 返回值形态完全不同,examples/src/routeguide/server.rs 提供了完整参考实现:
- 简单 RPC(
get_feature):接收Request<Point>,返回Result<Response<Feature>, Status>,本质是一次普通函数调用; - 服务端流式(
list_features):需要返回一个流。典型做法是用tokio::sync::mpsc::channel开异步任务生产数据,再把ReceiverStream包进tonic::Response返回:
type ListFeaturesStream = ReceiverStream<Result<Feature, Status>>; async fn list_features( &self, request: Request<Rectangle>, ) -> Result<Response<Self::ListFeaturesStream>, Status> { let (tx, rx) = mpsc::channel(4); let features = self.features.clone(); tokio::spawn(async move { for feature in &features[..] { if in_range(feature.location.as_ref().unwrap(), request.get_ref()) { tx.send(Ok(feature.clone())).await.unwrap(); } } }); Ok(Response::new(ReceiverStream::new(rx))) }- 客户端流式(
record_route):方法接收Request<tonic::Streaming<Point>>,用stream.next().await逐个消费输入流,同时折叠出统计结果,流结束后返回单个RouteSummary; - 双向流式(
route_chat):接收输入流、返回输出流,常借助async-stream的try_stream!宏做“流到流”的异步变换:
type RouteChatStream = Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>; async fn route_chat( &self, request: Request<tonic::Streaming<RouteNote>>, ) -> Result<Response<Self::RouteChatStream>, Status> { let mut notes = HashMap::new(); let mut stream = request.into_inner(); let output = async_stream::try_stream! { while let Some(note) = stream.next().await { let note = note?; let location = note.location.unwrap(); let location_notes = notes.entry(location).or_insert(vec![]); location_notes.push(note); for note in location_notes { yield note.clone(); } } }; Ok(Response::new(Box::pin(output) as Self::RouteChatStream)) }客户端调用流式方法时,返回流的消费方式是stream.message().await?循环读取,直到流结束(见 examples/src/routeguide/client.rs 中的print_features)。客户端侧发送流时,可以用tokio_stream::iter(集合)把Vec廉价地转成Stream再包进Request。
运行完整示例(仓库根目录执行):
$ cargo run --bin routeguide-server $ cargo run --bin routeguide-client # 另开终端客户端终端会以每秒一行的节奏打印双向流式 RPC 的输出,形如:
NOTE = RouteNote { location: Some(Point { latitude: 409146139, longitude: -746188906 }), message: "at 1.000319208s" }代码生成配置:默认配置之外的工作流
routeguide 教程 的附录专门讲解了tonic_prost_build的配置。默认的代码生成配置适合自包含示例与小项目,但当遇到以下场景时就需要定制:
- 在不同 crate里分别构建 Rust 客户端与服务端;
- 在更大的多语言项目中只构建 Rust 侧的一部分;
- 编辑器无法索引默认位置(
OUT_DIR)下生成的文件,想要 IDE 支持。
tonic_prost_build可以通过configure()链式方法定制输出:例如把.proto定义留在独立 crate、按需(而非构建时)生成代码,并指定输出目录:
fn main() { tonic_prost_build::configure() .build_client(false) // 只生成服务端代码 .out_dir("another_crate/src/pb") // 输出到指定目录 .compile_protos(&["path/my_proto.proto"], &["path"]) .expect("failed to compile protos"); }另一种工作流是:将.proto定义放进独立 crate 后,直接以该 crate 作为依赖被其他 crate 引用。README 中 tonic-build(prost系的服务代码生成)与 tonic-prost 正是这一职责的承载者。仓库中 codegen/src/main.rs 与examples/generated/下的生成结果(如 helloworld/helloworld_grpc.pb.rs)可以让你直观了解生成代码的结构。
项目布局:一个完整 gRPC 生态的工作区
README 的 Project Layout 一节把仓库划分成若干可独立复用的 crate。结合根 Cargo.toml 的工作区成员,各模块定位如下:
| 目录 | 定位 |
|---|---|
| tonic | 通用 gRPC 与 HTTP/2 客户端/服务端实现(核心 crate,version 0.14.6) |
| tonic-build | 基于prost的服务代码生成 |
| tonic-prost | 面向prost编码的 codec 与集成层 |
| tonic-types | 基于prost的 gRPC 工具类型,含 gRPC Well Known Types 支持 |
| tonic-health | 标准 gRPC 健康检查服务实现,同时是 unary 与响应流式 RPC 的示范 |
| tonic-reflection | 基于 tonic 的 gRPC reflection 实现 |
| examples | 演示 TLS、负载均衡、双向流式等特性的 gRPC 示例 |
| interop | 互操作测试实现,覆盖大量 gRPC 特性场景 |
除此之外,仓库还包含面向 xDS 控制面集成的一族 crate:xds-client、xds-client-opentelemetry、tonic-xds、grpc-xds(gRPC 生态中的高级负载均衡与服务发现方案),以及曾经的grpc、grpc-protobuf、grpc-benchmark等模块。对生产环境有高级路由、重试、熔断、负载均衡需求的读者可深入 tonic-xds 与 xds-client 探索。
生产化要点小结
综合 README、教程与源码,把tonic投入生产前建议关注以下几点:
- 版本与 MSRV:当前工作区
rust-version = "1.88"(见 Cargo.toml),tonic0.14.6 要求 Rust 1.88 及以上;如需使用最新已发布代码,关注0.14.x分支而非正在变更的master。 - 按需开启特性:TLS(
tls-ring/tls-aws-lc二选一,可叠加tls-native-roots/tls-webpki-roots信任根)、压缩(gzip/deflate/zstd)默认都是关闭的,用到才开,避免引入多余依赖。 - 消息大小上限:默认解码 4MB / 编码
usize::MAX,超限场景要显式调大解码限制或对响应流做分页。 - 构建依赖:若使用
tonic_build::compile_protos()这类 API,构建机需安装protoc;纯 Rust 的tonic-prost-build路径则无此要求。 - 中间件与拦截器:
tonic内部基于tower与hyper,天然支持可组合的中间件栈——认证、限流、负载均衡(tower的limit、balance、load-shed均已接入channel/server特性,见 tonic/Cargo.toml),示例见 interceptor 示例 与 tower 示例。
获取帮助与参与贡献
README 建议的求助路径依次是:先查 API 文档;若没有答案,可在 Tonic Discord 频道提问;仍无法解决再开 issue 提交问题。仓库本身是只读的,欢迎按 CONTRIBUTING.md 的指引参与贡献。
项目采用 MIT 许可。除非明确声明,任何为 Tonic 贡献的代码默认按 MIT 条款授权,不附加额外条件。
- 后端
- RPC框架
【免费下载链接】grpc-rust
A native gRPC client & server implementation with async/await support.
相关推荐
OXChart自定义图表库入门指南:从安装到绘制第一个饼图的完整教程
OXChart自定义图表库入门指南:从安装到绘制第一个饼图的完整教程 OXChart是一款功能强大的自定义图表库,专为Android开发者打造,使用简单且支持灵
gRPC Ruby 错误与取消处理实战:基于 GRPC::BadStatus 的服务端抛出与客户端捕获指南
gRPC Ruby 错误与取消处理实战:基于 GRPC::BadStatus 的服务端抛出与客户端捕获指南 本篇技术指南聚焦 gRPC 官方仓库中 exampl
后端RPC框架微服务通信gRPC Server Reflection 服务端接入指南:基于 google.golang.org/grpc/reflection 的注册与实现解析
gRPC Server Reflection 服务端接入指南:基于 google.golang.org/grpc/reflection 的注册与实现解析 gRP
云原生集群管理虚拟化多集群
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考