12个版本

0.3.2 2024年1月11日
0.3.1 2024年1月8日
0.3.0 2023年12月28日
0.2.0 2023年10月4日
0.1.1 2023年3月3日

#305HTTP服务器

Download history 23/week @ 2024-04-22 14/week @ 2024-04-29 26/week @ 2024-05-13 37/week @ 2024-05-20 36/week @ 2024-05-27 49/week @ 2024-06-03 12/week @ 2024-06-10 56/week @ 2024-06-17 105/week @ 2024-06-24 7/week @ 2024-07-01 55/week @ 2024-07-08 20/week @ 2024-07-22 25/week @ 2024-07-29

每月101 次下载

MIT/Apache

50KB
823

Axum Connect-Web

通过Rust的Axum将基于protobuf的Connect-Web RPC框架引入。

Axum版本

  • axum-connect:0.3axum:0.7 兼容
  • axum-connect:0.2axum:0.6 兼容

功能 🔍

  • 无缝集成到现有的Axum HTTP应用程序中
  • 与Axum的API紧密相似
    • 提取状态和其他实现 RpcFromRequestPartsparts,就像在Axum中一样。
    • 返回任何实现 RpcIntoResponse 的类型,就像Axum一样。
  • 生成的类型和服务处理程序是强类型的,...
  • 处理程序强制执行语义上正确的HTTP 'parts'访问。
  • 允许用户像Axum一样推导 RpcIntoResponseRpcFromRequestParts
    • 注意:这些必须是Axum类型的派生,因为它们更具有限制性;您不再处理任意的HTTP,而是在HTTP上说话 connect-web RPC over HTTP。
  • 使用惯用的Axum/Rust封装 connect-web 错误处理。
  • 从单独的crate中的 *.proto 文件进行代码生成。
  • 所有其他Axum带来的惊人好处,如社区、文档和性能!

注意 ⚠️

我们在生产中使用 axum-connect,但不知道是否有更有头脑的人使用。它是用Rust编写的,这显然提供了一些编译器的保证,但尚未经过良好的测试或实战证明。请自行决定如何使用这些信息。

如果您使用 axum-connect,请让我知道!如果您发现错误,请提出问题。

入门 🤓

假定您具备使用Protobuf(包括IDL及其在RPC框架中的应用)和Axum的相关知识。

依赖项 👀

您需要2个axum-connect包,一个用于代码生成,另一个用于运行时使用。由于prost的工作方式,您还需要将其添加到自己的项目中。显然,您还需要axumtokio

# Note: axum-connect-build will fetch `protoc` for you.
cargo add --build axum-connect-build
cargo add axum-connect prost axum
cargo add tokio --features full

Protobuf文件 🥱

首先,创建必要的“hello world”proto服务定义。

proto/hello.proto

syntax = "proto3";

package hello;

message HelloRequest { string name = 1; }

message HelloResponse { string message = 1; }

service HelloWorldService {
  rpc SayHello(HelloRequest) returns (HelloResponse) {}
}

代码生成 🤔

使用axum_connect_codegen包将proto IDL生成Rust代码。

目前,所有代码生成都是在本地磁盘上的proto文件中完成的,并使用一个build.rs文件。将来我希望支持更多Buf的语法,例如远程代码生成

build.rs

use axum_connect_build::{axum_connect_codegen, AxumConnectGenSettings};

fn main() {
    // This helper will use `proto` as the import path, and globs all .proto
    // files in the `proto` directory.
    //
    // Note that you might need to re-save the `build.rs` file after updating
    // a proto file to get rust-analyzer to pickup the change. I haven't put
    // time into looking for a fix to that yet.
    let settings = AxumConnectGenSettings::from_directory_recursive("proto")
        .expect("failed to glob proto files");

    axum_connect_codegen(settings).unwrap();
}

有趣的部分 😁

无聊的部分都处理完毕后,让我们使用Axum实现我们的服务!

use async_stream::stream;
use axum::{extract::Host, Router};
use axum_connect::{futures::Stream, prelude::*};
use proto::hello::*;

mod proto {
    pub mod hello {
        include!(concat!(env!("OUT_DIR"), "/hello.rs"));
    }
}

#[tokio::main]
async fn main() {
    // Build our application with a route. Note the `rpc` method which was added by `axum-connect`.
    // It expect a service method handler, wrapped in it's respective type. The handler (below) is
    // just a normal Rust function. Just like Axum, it also supports extractors!
    let app = Router::new()
        .rpc(HelloWorldService::say_hello(say_hello_success))
        .rpc(HelloWorldService::say_hello_stream(say_hello_stream));

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3030")
        .await
        .unwrap();
    println!("listening on http://{:?}", listener.local_addr());
    axum::serve(listener, app).await.unwrap();
}

async fn say_hello_success(
  Host(host): Host,
  request: HelloRequest
) -> HelloResponse {
    HelloResponse {
        message: format!(
            "Hello {}! You're addressing the hostname: {}.",
            request.name, host
        ),
    }
}

发送它 🚀

为了测试它,手动尝试访问端点。

curl --location 'https://127.0.0.1:3030/hello.HelloWorldService/SayHello' \
--header 'Content-Type: application/json' \
--data '{ "name": "Alec" }'

从这里,您可以启动一个connect-web TypeScript/Go项目,以端到端强类型RPC调用您的API。

请求/响应部分 🙍‍♂️

请求和响应类型都是通过axum-connect派生的。一开始这可能会显得有些多余。

我们先从简单的开始,RpcIntoResponse。连接RPC不是任意的HTML请求,它们不能返回任意的HTML响应。例如,流式响应必须返回HTTP 200状态码,不管错误状态如何。按照Axum(非常棒)的范式,这是通过类型系统强制执行的。RPC处理器不能返回任意的HTML,而必须返回axum-connect知道如何转换为有效Connect响应的内容。

不那么直观的是,axum-connect派生了RpcFromRequestParts,这与Axum的FromRequestParts几乎相同。重要的是,FromRequestParts可以返回任意的HTML响应错误,这同样是一个问题。

Axum还允许FromRequest在处理程序中占用最后一个参数,该处理程序消耗了整个HTTP请求(包括主体)。由于axum-connect需要自己处理请求输入,所以没有与RPC处理器相对应的等效部分。

路线图 / 非目标 🛣️

  • 探索比RpcFromRequestParts更好的类型
    • 理想情况下,客户端只需要提供一个RpcIntoError,但我还没有完全思考这个问题。我知道在自定义类型上有PITA。
  • 重新设计错误响应以允许多个错误
  • 在生成的代码和运行时代码之间进行版本检查
  • 为向前兼容性制定计划
  • 使所有内容与connect-web对齐...
  • 全面集成测试

更多遥远的目标 🌜

  • 我很乐意也支持一个WASM准备好的客户端库
  • 使用buf.build支持远程代码生成和简化的proto处理
  • 支持gRPC调用
    • 我认为这很难做,我没有个人用例
  • 可能有一天会支持通过WebRTC进行BiDi流式传输
    • 这需要connect-web支持相同的协议
    • WebRTC流因为它们是DTLS/SRTP和弹性的
  • 用自定义的简单东西替换Prost

非目标 🙅

  • 支持 gRPC 所有的功能
    • 您已经可以通过 Axum 得到很多这样的功能,但 gRPC 是一个怪物,我不想复制它。这种复杂性对 Google 来说很有用,但对其他人来说却是个障碍。
  • 做一切并与其他所有东西集成
    • 我计划保持 axum-connect 高度专注于其核心功能。擅长它所做的事情,其他的事情则留给其他 crate。
    • 这是 Rust 的典型用法。做好一件事,然后把其他的事情留给其他 crate。

Prost 和 Protobuf 📖

Protoc 版本

如果需要或希望这样做,可以在 AxumConnectGenSettings 中配置已安装的 protoc 版本。将值设置为 None 将完全禁用下载。

理由

由于 Prost 停止提供 protoc 二进制文件(这是一个我不同意的决定),因此 axum-connect-build 内部使用 protoc-fetcher 来下载并解决一份 protoc 的副本。这比强迫每个构建环境(通常是 Heroku 和/或 Docker)预安装最新的 protoc 二进制文件要简单得多。如果不同意,或者需要遵守公司政策,或者构建环境离线,可以禁用此行为。

我希望有一天能用一个新的“精简”的 protoc 库来替换所有的东西,为 Rust 社区提供支持。这个库内置了解析器,只支持最新的 proto3 语法以及标准的 JSON 序列化格式,并且明确不支持许多很少使用的功能。但那一天还不是今天。

许可证 🧾

Axum-Connect 采用双重许可(任选其一)

依赖项

~10–15MB
~309K SLoC