#rpc #proto #service #http #client #generated #twirp

twirp-rs

为 Rust Twirp RPC 提供的异步兼容库

2 个版本

0.3.0 2024 年 7 月 5 日

#1086网络编程

Download history 168/week @ 2024-07-01 544/week @ 2024-07-08 497/week @ 2024-07-15 734/week @ 2024-07-22 1057/week @ 2024-07-29 2052/week @ 2024-08-05 1911/week @ 2024-08-12

5,761 每月下载量
3 个 Crates 中使用(直接使用 2 个)

MIT 许可证

46KB
907

twirp-rs

Twirp 是一种基于 HTTP 和 Protocol Buffers(proto)的 RPC 协议。该协议使用 HTTP URL 来指定 RPC 端点,并通过 HTTP 请求/响应体发送/接收 proto 消息。服务在 .proto 文件 中定义,使得通过自动生成不同语言的客户端和服务器来轻松实现 RPC 服务。

标准实现是 Go,这是该协议的 Rust 实现。Rust 协议缓冲支持由 prost 生态系统提供。

prost-twirp 不同,为服务端和访问 RPC 生成的 trait 是在 async 函数之上实现的。由于在 Rust 1.75 版本之前的 Rust 版本中不支持直接使用包含 async 函数的 trait,因此此 crate 使用 async_trait 宏来封装使它们正常工作所需的所有框架。

用法

查看 示例 以获取完整示例项目。

.proto 文件中定义服务和消息

// service.proto
package service.haberdash.v1;

service HaberdasherAPI {
   rpc MakeHat(MakeHatRequest) returns (MakeHatResponse);
}
message MakeHatRequest { }
message MakeHatResponse { }

在您的 Cargo.toml 中将 twirp-build crate 添加为构建依赖项(您还需要 prost-build

# Cargo.toml
[build-dependencies]
twirp-build = "0.3"
prost-build = "0.12"

在您的项目中添加一个 build.rs 文件以编译 protos 并生成 Rust 代码

fn main() {
    let proto_source_files = ["./service.proto"];

    // Tell Cargo to rerun this build script if any of the proto files change
    for entry in &proto_source_files {
        println!("cargo:rerun-if-changed={}", entry);
    }

    prost_build::Config::new()
        .service_generator(twirp_build::service_generator())
        .compile_protos(&proto_source_files, &["./"])
        .expect("error compiling protos");
}

这将生成可在 target/build/your-project-*/out/example.service.rs 中找到的代码。为了使用此代码,您需要实现定义的 proto 服务的 trait,并将服务处理器连接到 hyper 网络服务器。请参阅 示例 main.rs 以获取详细信息。

包含生成的代码,创建一个路由器,注册您的服务,然后在 hyper 服务器中提供服务这些路由

mod haberdash {
    include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
}

use axum::Router;
use haberdash::{MakeHatRequest, MakeHatResponse};

#[tokio::main]
pub async fn main() {
    let api_impl = Arc::new(HaberdasherApiServer {});
    let twirp_routes = Router::new()
        .nest(haberdash::SERVICE_FQN, haberdash::router(api_impl));
    let app = Router::new()
        .nest("/twirp", twirp_routes)
        .fallback(twirp::server::not_found_handler);

    let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    if let Err(e) = axum::serve(tcp_listener, app).await {
        eprintln!("server error: {}", e);
    }
}

// Define the server and implement the trait.
struct HaberdasherApiServer;

#[async_trait]
impl haberdash::HaberdasherApi for HaberdasherApiServer {
    async fn make_hat(&self, ctx: twirp::Context, req: MakeHatRequest) -> Result<MakeHatResponse, TwirpErrorResponse> {
        todo!()
    }
}

此代码创建了一个 axum::Router,然后将其传递给 axum::serve() 来处理网络。这种使用 axum::serve 的方法是可选的。构建 app 后,您也可以通过导入 twirp::tower::Service 并执行 app.call(request).await 来从任何基于 hyper 的服务器中调用它。

使用方法(客户端)

在客户端,您还将获得一个基于您 proto 中的 rpc 端点的生成的 twirp 客户端。包含生成的代码,创建一个客户端,并开始进行 rpc 调用。

mod haberdash {
    include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
}

use haberdash::{HaberdasherApiClient, MakeHatRequest, MakeHatResponse};

#[tokio::main]
pub async fn main() {
    let client = Client::from_base_url(Url::parse("https://127.0.0.1:3000/twirp/")?)?;
    let resp = client.make_hat(MakeHatRequest { inches: 1 }).await;
    eprintln!("{:?}", resp);
}

依赖项

~8–19MB
~278K SLoC