发布 Tonic 0.5

2021年7月9日

我们很高兴地宣布 Tonic 0.5 版本,这是一个用 Rust 原生实现的 gRPC。0.5 是一个重要的版本,并且已经开发了一段时间。

一些主要的新功能包括

gRPC-Web

gRPC-Web 是一种协议,允许客户端通过 HTTP/1.1 连接到 gRPC 服务,而不是通常的 HTTP/2。gRPC-Web 的一个常见用例是在浏览器中运行的 JavaScript 客户端。以前,这需要使用代理来将 HTTP/2 请求转换为 HTTP/1.1。

然而,新的 crate tonic-web 允许常规的 Tonic 服务器接受 gRPC-Web 请求,而无需外部代理。

启用 gRPC-Web 支持非常简单,只需:

use tonic::transport::Server;
// code generated by tonic-build
use hello_world::greeter_server::{GreeterServer, Greeter};

struct MyGreeter;

#[tonic::async_trait]
impl Greeter for MyGreeter {
    // ...
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "0.0.0.0:3000".parse().unwrap();

    let greeter = GreeterServer::new(MyGreeter);

    // enable grpc-web support for our `greeter` service
    let service = tonic_web::enable(greeter);

    Server::builder()
        // by default, tonic servers only accept http2 requests
        // so we have to enable receiving http1 as well
        .accept_http1(true)
        .add_service(service)
        .serve(addr)
        .await?;

    Ok(())
}

有关更多详细信息,请参阅 tonic-web crate

压缩

Tonic 现在可以透明地压缩和解压缩请求、响应和流。

在客户端上启用压缩可以这样做

let client = GreeterClient::new(channel)
    // compress requests
    .send_gzip()
    /// accept compressed responses
    .accept_gzip();

在服务器上可以这样做

let service = GreeterServer::new(greeter)
    // accept compressed requests
    .accept_gzip()
    // compress responses, if supported by the client
    .send_gzip();

请注意,这需要在 Tonic 和 tonic-build 上都启用 compression 功能。有关更多详细信息,请参阅文档

改进的 Tower 集成

Tonic 一直支持通过 TowerService trait 扩展客户端和服务器,但在 0.5 中,新的 Server::layer 方法使其更加容易。例如,我们可以通过使用 tower-http 中的中间件向服务添加跟踪和授权。

use tonic::transport::Server;
use tower_http::{
    auth::RequireAuthorizationLayer,
    trace::TraceLayer,
};

// The stack of middleware our service will be wrapped in
let layer = tower::ServiceBuilder::new()
    // High level tracing of requests and responses
    .layer(TraceLayer::new_for_grpc())
    // Authorize all requests using a token
    .layer(RequireAuthorizationLayer::bearer("my-secret-token"))
    // Convert our `ServiceBuilder` into a `tower::Layer`
    .into_inner();

Server::builder()
    // Apply our middleware stack to the server
    .layer(layer)
    .add_service(GreeterServer::new(MyGreeter)
    .serve(addr)
    .await?;

更灵活的拦截器

拦截器是轻量级中间件,除其他外,可用于修改传入请求的元数据,并可以选择使用 status 拒绝它们。

然而,Tonic 对拦截器的支持一直相当有限。例如,您无法组合多个拦截器,而必须使用 Tower 的 Service 抽象,虽然它更强大,但也需要更多代码来设置。

在 Tonic 0.5 中,我们有一个新的拦截器 API,它和以前一样易于使用,但现在在内部使用 Tower。这意味着拦截器可以像任何其他 Tower 中间件一样应用。例如,现在可以使用以下方法向客户端添加多个拦截器:

use tonic::{
  Request, Status,
  service::interceptor_fn,
  transport::Endpoint
};

fn intercept_one(req: Request<()>) -> Result<Request<()>, Status> {
    // ...
}

fn intercept_two(req: Request<()>) -> Result<Request<()>, Status> {
    // ...
}

let channel = Endpoint::from_static("http://[::1]:50051").connect_lazy()?;

let intercepted_channel = tower::ServiceBuilder::new()
    .layer(interceptor_fn(intercept_one))
    .layer(interceptor_fn(intercept_two))
    .service(channel);

let client = GreeterClient::new(intercepted_channel);

0.5 包括许多其他较小的功能和改进。变更日志包含所有详细信息。

和往常一样,如果您有疑问,可以在 Tokio Discord 服务器的 #tonic 频道中找到我们。

— David Pedersen (@davidpdrsn)