---
title: tonic (gRPC)
url: https://doc.liz6.com/rust/12-async-ecosystem/02-tonic-gRPC
locale: zh
area: rust
tags:
- rust
- 异步生态
date: 2026-06-30
modified: 2026-06-30
description: gRPC 用 protobuf 定义强类型的 service 接口——改了 .proto,所有调用方必须更新否则编译失败(编译器验证的合约,不是约定)。tonic 是 Rust 的 gRPC 实现,codegen 从 .proto 生成 Rust trait,streaming 类型(unary/client/ser…
---

# tonic (gRPC)

> gRPC 用 protobuf 定义强类型的 service 接口——改了 .proto,所有调用方必须更新否则编译失败(编译器验证的合约,不是约定)。tonic 是 Rust 的 gRPC 实现,codegen 从 .proto 生成 Rust trait,streaming 类型(unary/client/server/bidi)和 interceptor 层分别对应不同的使用场景和横切逻辑。

## gRPC 与 REST 的区别

REST 基于 HTTP 方法 + URL 表达操作，payload 通常是 JSON。gRPC 用 protobuf 定义强类型的 service 接口，payload 是二进制编码的 protobuf message。区别不仅是格式——而是**编程模型**。REST 的接口定义是"约定"（API doc + 样板）。gRPC 的接口定义是**编译器验证的合约**——改了 .proto，所有调用方必须更新否则编译失败。

tonic 是 Rust 生态中最成熟的 gRPC 实现，基于 tokio + tower + prost (protobuf 编译器)。

## 从 .proto 到 Rust 类型

```protobuf
syntax = "proto3";
package greeter;

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);           // unary: 1 request → 1 response
    rpc StreamHello (HelloRequest) returns (stream HelloReply); // server streaming
    rpc Chat (stream ChatMessage) returns (stream ChatMessage); // bidi streaming
}

message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
```

`build.rs` 中 `tonic_build::compile_protos()` 调用 protoc + prost 生成：
- `GreeterServer<T: Greeter>` — server wrapper（把 `Greeter` trait 包装为 tower `Service`）
- `Greeter` trait — 业务逻辑的接口（你实现的方法）
- `GreeterClient` — client stub（调用方使用的 async 方法）

生成的代码用 `#[tonic::async_trait]` 实现 trait 方法——这允许 trait 中包含 `async fn`（原生 Rust trait 尚不支持 async fn）。

## Server: 实现 trait

```rust
struct MyGreeter;
#[tonic::async_trait]
impl Greeter for MyGreeter {
    async fn say_hello(&self, req: Request<HelloRequest>) -> Result<Response<HelloReply>, Status> {
        let name = req.into_inner().name;
        if name.is_empty() {
            return Err(Status::invalid_argument("name is required"));
        }
        Ok(Response::new(HelloReply {
            message: format!("Hello {}!", name),
        }))
    }
}

tonic::transport::Server::builder()
    .add_service(GreeterServer::new(MyGreeter))
    .serve("[::1]:50051".parse().unwrap()).await?;
```

`Status` 是 gRPC 的标准错误类型——不是 HTTP status code。gRPC 定义了 17 个 status codes：`Ok`, `Cancelled`, `InvalidArgument`, `NotFound`, `AlreadyExists`, `PermissionDenied`, `Unauthenticated`, `ResourceExhausted`, `Unimplemented`, `Internal`, `Unavailable`, `DeadlineExceeded`。每个 code 有明确的语义——不像 HTTP 中 400 可能代表"格式错误"也可能代表"参数缺少"。

## Streaming: 不只是"一次请求一次响应"

```rust
// Server streaming: server 逐步发送数据
async fn stream_hello(&self, req: Request<HelloRequest>) -> Result<Response<Self::StreamHelloStream>, Status> {
    let mut stream = tokio_stream::iter(vec!["Hello", "Bonjour", "Hola"].into_iter()
        .map(|s| Ok(HelloReply { message: s.into() })));
    Ok(Response::new(Box::pin(stream)))
}

// Bidirectional streaming: 双方同时发送
async fn chat(&self, req: Request<Streaming<ChatMessage>>) -> Result<Response<Self::ChatStream>, Status> {
    let mut in_stream = req.into_inner();
    let (tx, rx) = tokio::sync::mpsc::channel(4);
    tokio::spawn(async move {
        while let Some(Ok(msg)) = in_stream.next().await {
            tx.send(Ok(ChatReply { response: format!("echo: {}", msg.text) })).await.unwrap();
        }
    });
    Ok(Response::new(Box::pin(tokio_stream::wrappers::ReceiverStream::new(rx))))
}
```

streaming 的关键：server streaming = server 返回 `stream T`，client 逐个接收。bidirectional = 两端各自发送 `stream`。这与 WebSocket 的"双向通道"不同——gRPC streaming 的每个消息仍是独立的、有类型的 protobuf message。

## Interceptor: 请求级别的中间件

```rust
fn auth_interceptor(mut req: Request<()>) -> Result<Request<()>, Status> {
    let token = req.metadata().get("authorization")
        .ok_or_else(|| Status::unauthenticated("missing token"))?;
    validate_token(token)?;
    Ok(req)
}

Server::builder()
    .add_service(GreeterServer::with_interceptor(MyGreeter, auth_interceptor))
```

interceptor 在 handler 之前执行——可以做认证、限流、metadata 注入。与 tower Layer 的区别：interceptor 是 tonic 的专属概念，Layer 是 tower 的通用概念。tonic 的 interceptor 实际上就是 tower `Service` 的一个薄封装。

## 参考

- **tonic**: github.com/hyperium/tonic
- **gRPC**: grpc.io (spec, status codes)
- **protobuf**: protobuf.dev (encoding, proto3 language guide)

*Keywords: tonic, gRPC, protobuf, streaming, interceptor, Status, unary, bidirectional*
