尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

如何用 hyper 低层 API 直接驱动 axum Router

如何用 hyper 低层 API 直接驱动 axum Router 如何用 hyper 低层 API 直接驱动 axum Router【免费下载链接】axumHTTP routing and request-handling library for Rust that focuses on ergonomics and modularity项目地址: https://gitcode.com/GitHub_Trending/ax/axumaxum 默认通过axum::serve启动但它的文档明确说明这种运行方式刻意保持简单不支持太多配置连接层直接应用 hyper 的默认配置包括超时需要更多控制时应该改用 hyper 或 hyper-util见 serve 模块文档。当你不想依赖axum::serve的黑盒而要自己接管连接接受、服务桥接和连接构造过程时就可以用 hyper 的低层 API 直接驱动Router。仓库中的serve-with-hyper示例就是为此准备的本文按照该示例还原完整操作路径。准备条件依赖与 feature示例的完整源码见 examples/serve-with-hyper/src/main.rs依赖声明见 examples/serve-with-hyper/Cargo.toml。在 axum 仓库内运行它不需要额外配置因为examples是一个独立 workspace见 examples/Cargo.toml。在独立项目中复刻这个接入方式时示例的Cargo.toml给出了所需依赖版本与 feature[dependencies] hyper { version 1.0, features [] } hyper-util { version 0.1, features [tokio, server-auto, http1] } tokio { version 1.0, features [full] } tower { version 0.5.2, features [util] }注意示例中axum用的是path ../../axum指向仓库内源码独立项目需要换成自己的 axum 依赖。示例文件头部还提示hyper-utilcrate 虽然提供了高层工具但仍处于早期开发阶段见 示例源码注释。tower依赖的utilfeature 在这里是必需的后面的代码要用ServiceExt::oneshot调用 tower 服务。基本接入接受连接并把 Router 桥接成 hyper Service核心代码对应示例中的serve_plain函数可直接从 main.rs 复制async fn serve_plain() { // 创建普通的 axum 应用 let app Router::new().route(/, get(|| async { Hello! })); // 用 tokio 创建 TcpListener let listener TcpListener::bind(0.0.0.0:3000).await.unwrap(); // 持续接受新连接 loop { // 本示例丢弃远端地址如何暴露地址见 serve_with_connect_info let (socket, _remote_addr) listener.accept().await.unwrap(); // Router 始终 ready不需要调用 poll_ready let tower_service app.clone(); // 每个连接 spawn 一个任务从而并发处理多个连接 tokio::spawn(async move { // hyper 有自己的一套 AsyncRead/AsyncWrite trait不用 tokio 的。 // TokioIo 负责在两者之间转换。 let socket TokioIo::new(socket); // hyper 也有自己的 Service trait不用 tower。 // 用 hyper::service::service_fn 创建一个 hyper Service // 通过 tower::Service::call 调用 axum 应用。 let hyper_service hyper::service::service_fn(move |request: RequestIncoming| { // hyper 的 Service 用 selftower 的 Service 需要 mut self // 所以必须 clone tower_service。 // Router 始终 ready不需要 poll_ready。 tower_service.clone().call(request) }); // server::conn::auto::Builder 同时支持 http1 和 http2。 // TokioExecutor 告诉 hyper 用 tokio::spawn 派生任务。 if let Err(err) server::conn::auto::Builder::new(TokioExecutor::new()) // 处理 websocket 需要 serve_connection_with_upgrades // 不需要的话可以用 serve_connection。 .serve_connection_with_upgrades(socket, hyper_service) .await { eprintln!(failed to serve connection: {err:#}); } }); } }对应的导入部分与示例一致use std::net::SocketAddr; use axum::extract::ConnectInfo; use axum::{extract::Request, routing::get, Router}; use hyper::body::Incoming; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server; use tokio::net::TcpListener; use tower::{Service, ServiceExt};这条接入路径的关键判断点TokioIo不可省略hyper 的AsyncRead/AsyncWrite与 tokio 的不是同一套 traitTokioIo::new(socket)是唯一的转换入口。service_fn是 tower 到 hyper 的桥axum 的Router是 towerService而serve_connection_with_upgrades要求 hyperService桥接闭包里每次请求 clone 一次 tower 服务再call。两个 clone 各有原因外层 clone 是为了拿到可move进任务的服务内层 clone 是因为 hyper 的Service基于self、tower 的基于mut self。示例注释同时说明Router始终 ready因此全程不需要poll_ready。autoBuilderserver::conn::auto::Builder同时支持 HTTP/1 和 HTTP/2TokioExecutor::new()指定 hyper 内部任务用tokio::spawn派生。升级支持需要 websocket 时用serve_connection_with_upgrades不需要时用serve_connection即可见 示例注释。连接处理失败时示例只会eprintln!(failed to serve connection: {err:#})这是示例给出的唯一错误输出方式。可选分支让 handler 拿到客户端远端地址基本接入里remote_addr被直接丢弃。如果 handler 需要客户端地址用into_make_service_with_connect_info把远端地址交给ConnectInfoextractor。示例中的serve_with_connect_info展示了这条变体main.rsasync fn serve_with_connect_info() { let app Router::new().route( /, get( |ConnectInfo(remote_addr): ConnectInfoSocketAddr| async move { format!(Hello {remote_addr}) }, ), ); let mut make_service app.into_make_service_with_connect_info::SocketAddr(); let listener TcpListener::bind(0.0.0.0:3001).await.unwrap(); loop { let (socket, remote_addr) listener.accept().await.unwrap(); // IntoMakeServiceWithConnectInfo 始终 ready不需要 poll_ready let tower_service unwrap_infallible(make_service.call(remote_addr).await); tokio::spawn(async move { let socket TokioIo::new(socket); let hyper_service hyper::service::service_fn(move |request: RequestIncoming| { tower_service.clone().oneshot(request) }); if let Err(err) server::conn::auto::Builder::new(TokioExecutor::new()) .serve_connection_with_upgrades(socket, hyper_service) .await { eprintln!(failed to serve connection: {err:#}); } }); } } fn unwrap_infallibleT(result: ResultT, Infallible) - T { match result { Ok(value) value, Err(err) match err {}, } }与基本接入的差异有三处理解它们就能判断是否要采用这条变体Router先经过into_make_service_with_connect_info::SocketAddr()转成MakeService它接收remote_addr并产出把ConnectInfo存入请求 extension 的服务每个连接要用make_service.call(remote_addr).await拿到该连接专属的 tower 服务返回值是Result_, Infallible示例用unwrap_infallible解包调用时改用ServiceExt::oneshot(request)而不是clone().call(request)因为MakeService产出的服务是单连接消费型的。关于into_make_service_with_connect_info的用法和自定义Connected类型可继续看 axum/src/docs/routing/into_make_service_with_connect_info.md。运行与验证示例的文件头注释给出了运行命令在 axum 仓库根目录下执行cargo run -p example-serve-with-hypermain函数用tokio::join!(serve_plain(), serve_with_connect_info())同时启动两个服务分别监听0.0.0.0:3000基本接入handler 固定返回Hello!0.0.0.0:3001ConnectInfo 变体handler 返回Hello {remote_addr}即把实际客户端地址拼进响应。按示例代码本身验证方式就是对两个端口分别发起GET /请求3000 端口应返回示例定义的字符串Hello!3001 端口应返回Hello加上发起请求的客户端地址具体地址值取决于你的客户端文档未给出固定值。如果连接处理出错终端会打印failed to serve connection: {err:#}这是示例代码中唯一的错误输出路径。边界与限制为什么不用axum::serveserve 文档 说明它刻意简单hyper 的默认配置含header_read_timeout这类超时直接生效要自定义连接构建过程就用 hyper / hyper-util 低层 API——这正是本文路径的适用场景。hyper-util的成熟度示例文件头部注明该 crate 仍处于早期开发阶段选择这条路径时应了解这一点。HTTP 版本本文示例的hyper-utilfeature 只启用了http1见 示例 Cargo.toml但server::conn::auto::Builder本身同时支持 http1 和 http2需要 HTTP/2 时按自己项目的需求配置对应 feature。超时行为连接层超时由 hyper 默认配置决定axum::serve文档将其指向hyper::server::conn::http1::Builder::header_read_timeout示例代码没有做额外调整。这条低层路径没有提供with_graceful_shutdown之类的收尾逻辑——那是axum::serve的 API示例中没有对应的低层写法。需要优雅关停或自定义Executor时回到 axum/src/serve/mod.rs 查看Serve::with_graceful_shutdown和Serve::with_executor的文档示例。【免费下载链接】axumHTTP routing and request-handling library for Rust that focuses on ergonomics and modularity项目地址: https://gitcode.com/GitHub_Trending/ax/axum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表