10 个版本
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 日 |
#1414 在 HTTP 服务器 中
每月 221 次下载
21KB
210 行
Axum Connect-Web
通过 Rust 的惯用方法将基于 protobuf 的 Connect-Web RPC 框架 带入 Axum。
Axum 版本
axum-connect:0.3
与axum:0.7
兼容axum-connect:0.2
与axum:0.6
兼容
特性 🔍
- 无缝集成到现有的 Axum HTTP 应用程序中
- 紧密映射 Axum 的 API
- 提取状态和其他 impl
RpcFromRequestParts
的parts
,就像使用 Axum 一样。 - 返回任何 impl
RpcIntoResponse
的类型,就像 Axum 一样。
- 提取状态和其他 impl
- 生成的类型和服务处理器是强类型的,...
- 处理器强制执行语义上正确的 HTTP 'parts' 访问。
- 允许用户像 Axum 一样推导
RpcIntoResponse
和RpcFromRequestParts
。- 注意:这些必须是 Axum 类型的派生,因为它们更严格;你不再处理任意的 HTTP,你正在通过 HTTP 传输
connect-web
RPC。
- 注意:这些必须是 Axum 类型的派生,因为它们更严格;你不再处理任意的 HTTP,你正在通过 HTTP 传输
- 在惯用的 Axum/Rust 中包装
connect-web
错误处理。 - 从单独的 crate 中的
*.proto
文件中进行代码生成。 - 所有其他 Axum 的美妙好处,如社区、文档和性能!
注意 ⚠️
在生产环境中,我们使用axum-connect
,但不知道有谁会用得更明智。它是用Rust编写的,显然提供了一些令人惊叹的编译器保证,但还没有经过良好的测试或实战检验。对这条信息随心所欲吧。
请告诉我你是否在使用axum-connect
!如果你发现任何错误,请创建问题。
入门 🤓
假定您对Protobuf(包括IDL及其在RPC框架中的应用)和Axum有先前的了解。
依赖关系 👀
您需要2个axum-connect
包,一个用于代码生成,一个用于运行时使用。由于prost的工作方式,您还需要将其添加到自己的项目中。显然,您还需要axum
和tokio
。
# 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 'http://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
知道如何将其转换为有效连接响应的内容。
不那么直观的是,axum-connect
派生了RpcFromRequestParts
,它与Axum的FromRequestParts
几乎相同。重要的是,FromRequestParts
可以返回任意HTML响应的错误,这同样是一个问题。
Axum还允许FromRequest
在处理器中占用最后一个参数,该处理器消耗了整个HTTP请求(包括正文)。由于axum-connect
需要自己处理请求输入,因此没有为RPC处理器提供等效的选项。
路线图/未实现的目标 🛣️
- 探索比
RpcFromRequestParts
更好的类型- 理想情况下,客户端只需提供一个
RpcIntoError
,但我还没有完全想通这个问题。我只知道,对于自定义类型,需要同时指定FromRequestParts
和RpcFromRequestParts
是一个很烦人的事情。
- 理想情况下,客户端只需提供一个
- 重新设计错误响应以允许多个错误
- 生成代码和运行时代码之间的版本检查
- 向前兼容性的计划
- 使一切与
connect-web
保持一致,并... - 全面的集成测试
更长远的目标 🌜
- 我还希望能支持一个WASM兼容的客户库
- 使用
buf.build
来支持远程代码生成和简化的proto处理 - 支持gRPC调用
- 我认为这不难,只是我没有个人用例
- 可能也许有一天会支持通过WebRTC的双向流
- 这需要
connect-web
支持相同的功能 - WebRTC 流,因为它们是 DTLS/SRTP 并且具有弹性
- 这需要
- 用自定义的简单方法替换 Prost
非目标 🙅
- 支持 gRPC 的所有功能
- 您已经在 Axum 中得到了很多这样的功能,但 gRPC 是一个怪物,我不希望复制。这种复杂性对 Google 有用,但对几乎所有其他人来说都是障碍。
- 做一切事情并与一切集成
- 我计划使
axum-connect
非常专注。擅长它所做的事情,其他什么都不做。 - 这是 Rust 的惯例。做好一件事情,其余的留给其他 crate 处理。
- 我计划使
Prost 和 Protobuf 📖
Protoc 版本
如果您需要或希望这样做,可以配置已安装的 protoc
版本为 None
,这将完全禁用下载。
原因
Prost 停止提供 protoc
二进制文件(这是一个我不同意的决定),因此 axum-connect-build
内部使用 protoc-fetcher 来下载和解析 protoc
的副本。这比强制每个构建环境(通常是 Heroku 和/或 Docker)预先安装最新的 protoc
二进制文件要方便得多。如果不同意,或者需要遵守公司政策,或者构建环境离线,可以禁用此行为。
我希望有一天能用一个新的“轻量级”protoc 库来替换这一切,这个库具有内置解析器,仅支持最新的 proto3 语法以及标准 JSON 序列化格式,并且明确不支持许多很少使用的功能。但那一天不是今天。
许可证 🧾
Axum-Connect 是双许可(由您选择)
- MIT 许可证(《LICENSE-MIT》或 http://opensource.org/licenses/MIT》)
- Apache 许可证 2.0(《LICENSE-APACHE》或 http://apache.ac.cn/licenses/LICENSE-2.0”)
依赖项
~12–30MB
~450K SLoC