Rust 异步编程入门:用 Tokio + Axum 写一个 HTTP 服务

举报
yd_232564628 发表于 2026/10/21 10:47:37 2026/10/21
【摘要】 前两篇聊了 Rust 为什么值得学、怎么用 Rust 做小工具和桌面应用。这篇把镜头拉到后端:用 Rust 写一个真正的 HTTP 服务。Rust 做后端最大的吸引力不是“能跑”,而是稳定、可控、资源占用低。一个 Rust 服务启动后内存可能只有十几兆,没有 GC 停顿,错误在编译期就被拦下来,部署也只需要一个二进制文件。 异步 Rust 是什么?简单说,异步就是“一个线程可以同时等待很多任...

前两篇聊了 Rust 为什么值得学、怎么用 Rust 做小工具和桌面应用。这篇把镜头拉到后端:用 Rust 写一个真正的 HTTP 服务。

Rust 做后端最大的吸引力不是“能跑”,而是稳定、可控、资源占用低。一个 Rust 服务启动后内存可能只有十几兆,没有 GC 停顿,错误在编译期就被拦下来,部署也只需要一个二进制文件。

异步 Rust 是什么?

简单说,异步就是“一个线程可以同时等待很多任务”。

比如服务器同时收到 100 个请求,同步写法可能需要 100 个线程;异步写法则可以让一个线程在等待数据库、网络、文件 IO 时,先去处理其他请求。

Rust 标准库本身不提供异步运行时,所以实际开发中通常会用 Tokio。Tokio 是 Rust 生态里最成熟的异步运行时,负责调度任务、处理 IO、管理线程池。

而 Axum 是建立在 Tokio 生态上的 Web 框架,类型设计非常 Rust:路由、中间件、错误处理都尽量利用类型系统,写起来简洁,出错时编译器也会给出明确提示。

为什么推荐 Tokio + Axum?

Tokio 和 Axum 的组合有几个明显优势:

  • 生态成熟:Tokio 被大量生产级项目使用,文档和社区都很完善。
  • 类型友好:Axum 的路由和处理器天然适配 Rust 的类型系统,参数提取、状态共享、错误返回都很直观。
  • 性能稳定:没有 GC,请求延迟更可预测,适合高并发场景。
  • 部署简单:编译成单个二进制,扔到服务器上就能跑。

它当然也有学习成本。你需要理解 async/await、Future、Send + Sync、生命周期和所有权在异步环境里的表现。但这些概念一旦打通,写出来的后端代码会非常扎实。

第一个 Axum 服务

先初始化项目:

cargo new hello_axum
cd hello_axum
cargo add axum tokio --features tokio/full

然后写一个最简单的服务:

use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(|| async { "Hello, Rust!" }));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();

    println!("Listening on http://0.0.0.0:3000");
    axum::serve(listener, app).await.unwrap();
}

运行后访问 http://localhost:3000,就能看到返回内容。这个例子很小,但已经包含了 Rust 异步服务的核心结构:Router + Handler + Listener。

路由和参数提取

Axum 的参数提取非常舒服。比如要写一个根据 ID 查询用户的接口:

use axum::{extract::Path, routing::get, Json, Router};
use serde::Serialize;

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
}

async fn get_user(Path(id): Path<u64>) -> Json<User> {
    Json(User {
        id,
        name: format!("User {id}"),
    })
}

let app = Router::new().route("/users/:id", get(get_user));

这里没有手动解析 URL,也没有到处做字符串转换。Axum 会根据类型自动提取参数,如果类型不匹配,请求会直接返回 404 或 400。

共享状态

真实服务通常需要共享数据库连接、配置或缓存。Axum 提供了 State 机制:

use axum::{extract::State, routing::get, Json, Router};
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    app_name: String,
}

async fn info(State(state): State<Arc<AppState>>) -> Json<AppState> {
    Json(state.as_ref().clone())
}

let state = Arc::new(AppState {
    app_name: "hello_axum".into(),
});

let app = Router::new()
    .route("/info", get(info))
    .with_state(state);

Arc 保证状态可以在线程间共享,Clone 让每个路由处理器都能方便地拿到副本。这个模式在 Axum 项目里非常常见。

错误处理要提前想清楚

Rust 后端开发里,错误处理几乎一定会遇到。Axum 本身提供了 IntoResponse,你可以把自定义错误类型转换成 HTTP 响应。

一个比较实用的做法是定义自己的 AppError:

use axum::{http::StatusCode, response::IntoResponse};

enum AppError {
    NotFound,
    Internal,
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "not found").into_response(),
            AppError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "internal error").into_response(),
        }
    }
}

这样业务逻辑里就可以返回 Result<T, AppError>,而不是到处 unwrap()。

从练习到真实项目

一个完整的 Rust 后端项目通常还会涉及:

  • 数据库:SQLx、SeaORM、Diesel
  • 配置管理:dotenvy、config
  • 日志与监控:tracing、tracing-subscriber
  • 认证授权:JWT、Session、OAuth
  • API 文档:utoipa、OpenAPI
  • 测试:axum::TestClient、集成测试
  • 部署:Docker、静态编译、CI/CD

Tokio 和 Axum 的生态已经足够支撑这些需求,很多团队也确实把它们用于生产环境。

异步 Rust 的学习建议

不要一开始就追求“高性能压测”。更稳的路径是:

  1. 先跑通一个返回 JSON 的 HTTP 服务
  2. 加上路径参数和查询参数
  3. 引入 State 共享配置
  4. 定义自己的错误类型
  5. 接入数据库或本地文件存储
  6. 加上日志和简单测试
  7. 最后再考虑连接池、缓存、并发优化

Rust 异步编程确实比同步代码复杂一点,但它的回报也很清晰:更少运行时意外,更可控的资源使用,更明确的错误边界。

如果你已经写过一些 Rust 小工具,或者对后端、云原生、高性能服务感兴趣,Tokio + Axum 会是一个非常好的下一步。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。