#rpc #service #build #proto #interface #define #twirp

twirp-build-rs

异步兼容 Twirp RPC 接口的代码生成

2 个版本

0.3.0 2024年7月5日

#45 in #proto

Download history 162/week @ 2024-07-01 32/week @ 2024-07-08 4/week @ 2024-07-29 246/week @ 2024-08-05 159/week @ 2024-08-12

409 次每月下载
sp1-server 中使用

MIT 许可证

10KB
93

twirp-rs

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

规范实现是 Go 语言版本,这是该协议的 Rust 语言实现。Rust 协议缓冲支持由 prost 生态系统提供。

prost-twirp 不同,生成用于服务和访问 RPC 的特质是在 async 函数之上实现的。由于在 Rust 1.75 之前的版本中,不支持直接使用包含 async 函数的特质 ,因此此存储库使用 async_trait 宏来封装使其工作所需的框架。

使用方法

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

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

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

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

twirp-build 存储库添加为您的 Cargo.toml 中的构建依赖项(您还需要 prost-build

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

向您的项目添加 build.rs 文件以编译 proto 并生成 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 中生成代码。为了使用此代码,您需要实现定义的服务对应的特质,并将服务处理程序连接到 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的服务器调用它。

用法(客户端)

在客户端,你还会得到一个生成的twirp客户端(基于你的proto中的rpc端点)。包含生成的代码,创建一个客户端,并开始执行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);
}

依赖项

~7–17MB
~231K SLoC