【Axum】Rust Web 高效构建:Axum 框架从入门到精通指南
目录
- 一、环境准备与项目创建
- 1.1 安装 Rust 工具链
- 1.2 创建项目并添加依赖
- 二、Axum 核心架构解析
- 三、项目结构设计
- 四、核心代码实现
- 4.1 应用入口 (src/main.rs)
- 4.2 数据模型 (src/models.rs)
- 4.3 路由配置 (src/routes.rs)
- 4.4 认证服务 (src/services/auth.rs)
- 4.5 用户处理器 (src/handlers.rs)
- 4.6 数据访问层 (src/repositories/user_repository.rs)
- 五、认证中间件实现
- 5.1 认证中间件 (src/middleware/auth.rs)
- 六、数据库迁移脚本
- 七、性能优化策略
- 7.1 连接池配置优化
- 7.2 异步任务处理
- 7.3 缓存策略实现
- 八、测试与部署
- 8.1 API 测试脚本
- 8.2 Docker 部署配置
- 九、性能测试结果
- 总结
Axum 是基于 Tokio 和 Hyper 构建的高性能 Rust Web 框架,以其简洁的 API 设计和卓越的性能表现成为现代 Rust Web 开发的首选。本文将带你从零开始构建一个完整的 RESTful API 服务,涵盖路由设计、数据库集成、认证授权等核心功能。
一、环境准备与项目创建
1.1 安装 Rust 工具链
curl--proto'=https'--tlsv1.2-sSf https://sh.rustup.rs |sh rustup update stable 1.2 创建项目并添加依赖
cargo new axum-api cd axum-api 修改 Cargo.toml:
[package] name = "axum-api" version = "0.1.0" edition = "2021" [dependencies] axum = { version = "0.7", features = ["headers", "json"] } tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] } tower-http = { version = "0.5", features = ["cors", "trace"] } dotenvy = "0.15" chrono = "0.4" uuid = { version = "1.4", features = ["v4"] } bcrypt = "0.15" jsonwebtoken = "9.0" 二、Axum 核心架构解析
客户端路由器 Router中间件 Middleware处理函数 Handler服务层 Service数据访问层 Repository数据库 Database
三、项目结构设计
src/ ├── main.rs # 应用入口 ├── routes.rs # 路由配置 ├── handlers.rs # 请求处理器 ├── models.rs # 数据模型 ├── services.rs # 业务逻辑 ├── repositories.rs # 数据访问 ├── utils.rs # 工具函数 └── errors.rs # 错误处理 四、核心代码实现
4.1 应用入口 (src/main.rs)
useaxum::{Router,Extension};usedotenvy::dotenv;usesqlx::postgres::PgPoolOptions;usestd::env;usestd::net::SocketAddr;usetower_http::cors::CorsLayer;usetower_http::trace::TraceLayer;modroutes;modmodels;modhandlers;modservices;modrepositories;moderrors;modutils;#[tokio::main]asyncfnmain(){// 初始化日志tracing_subscriber::fmt::init();// 加载环境变量dotenv().ok();// 创建数据库连接池let database_url =env::var("DATABASE_URL").expect("DATABASE_URL must be set");let pool =PgPoolOptions::new().max_connections(50).connect(&database_url).await.expect("Failed to create pool");// 初始化路由let app =Router::new().merge(routes::user_routes()).merge(routes::auth_routes()).layer(Extension(pool)).layer(CorsLayer::permissive()).layer(TraceLayer::new_for_http());// 启动服务器let addr =SocketAddr::from(([0,0,0,0],3000));tracing::info!("服务器启动在 http://{}", addr);axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();}4.2 数据模型 (src/models.rs)
useserde::{Deserialize,Serialize};usesqlx::FromRow;usechrono::{DateTime,Utc};useuuid::Uuid;#[derive(Debug, Serialize, Deserialize, FromRow)]pubstructUser{pub id:Uuid,pub username:String,pub email:String,#[serde(skip_serializing)]pub password_hash:String,pub created_at:DateTime<Utc>,pub updated_at:DateTime<Utc>,}#[derive(Debug, Deserialize)]pubstructCreateUser{pub username:String,pub email:String,pub password:String,}#[derive(Debug, Deserialize)]pubstructLoginUser{pub email:String,pub password:String,}#[derive(Debug, Serialize)]pubstructAuthResponse{pub token:String,pub user:User,}4.3 路由配置 (src/routes.rs)
useaxum::{routing::{get, post},Router,};usecrate::handlers::{create_user_handler, get_users_handler, login_handler, get_current_user};pubfnuser_routes()->Router{Router::new().route("/users",post(create_user_handler)).route("/users",get(get_users_handler))}pubfnauth_routes()->Router{Router::new().route("/auth/login",post(login_handler)).route("/auth/me",get(get_current_user))}4.4 认证服务 (src/services/auth.rs)
usecrate::{models::User,errors::AppError};usebcrypt::{hash, verify,DEFAULT_COST};usejsonwebtoken::{encode, decode,Header,EncodingKey,DecodingKey,Validation};usechrono::{Utc,Duration};useserde::{Serialize,Deserialize};useuuid::Uuid;constSECRET_KEY:&str="your_very_secret_key";#[derive(Debug, Serialize, Deserialize)]pubstructClaims{pub sub:Uuid,// 用户IDpub exp:usize,// 过期时间}pubfnhash_password(password:&str)->Result<String,AppError>{hash(password,DEFAULT_COST).map_err(|_|AppError::InternalServerError)}pubfnverify_password(password:&str, hash:&str)->Result<bool,AppError>{verify(password, hash).map_err(|_|AppError::InternalServerError)}pubfncreate_jwt(user_id:Uuid)->Result<String,AppError>{let expiration =Utc::now().checked_add_signed(Duration::hours(24)).expect("valid timestamp").timestamp()asusize;let claims =Claims{ sub: user_id, exp: expiration,};encode(&Header::default(),&claims,&EncodingKey::from_secret(SECRET_KEY.as_bytes()),).map_err(|_|AppError::InternalServerError)}pubfndecode_jwt(token:&str)->Result<Claims,AppError>{decode::<Claims>( token,&DecodingKey::from_secret(SECRET_KEY.as_bytes()),&Validation::default(),).map(|data| data.claims).map_err(|_|AppError::Unauthorized)}4.5 用户处理器 (src/handlers.rs)
useaxum::{extract::{Extension,Json},response::JsonasJsonResponse,http::StatusCode,};usesqlx::PgPool;usecrate::{models::{User,CreateUser,LoginUser,AuthResponse},services::{self,auth::create_jwt},repositories::user_repository,errors::AppError,};pubasyncfncreate_user_handler(Extension(pool):Extension<PgPool>,Json(payload):Json<CreateUser>,)->Result<JsonResponse<AuthResponse>,AppError>{let hashed_password =services::auth::hash_password(&payload.password)?;let user =user_repository::create_user(&pool,&payload.username,&payload.email,&hashed_password).await?;let token =create_jwt(user.id)?;Ok(JsonResponse(AuthResponse{ token, user }))}pubasyncfnlogin_handler(Extension(pool):Extension<PgPool>,Json(payload):Json<LoginUser>,)->Result<JsonResponse<AuthResponse>,AppError>{let user =user_repository::find_user_by_email(&pool,&payload.email).await?.ok_or(AppError::Unauthorized)?;let is_valid =services::auth::verify_password(&payload.password,&user.password_hash)?;if!is_valid {returnErr(AppError::Unauthorized);}let token =create_jwt(user.id)?;Ok(JsonResponse(AuthResponse{ token, user }))}pubasyncfnget_users_handler(Extension(pool):Extension<PgPool>,)->Result<JsonResponse<Vec<User>>,AppError>{let users =user_repository::get_all_users(&pool).await?;Ok(JsonResponse(users))}pubasyncfnget_current_user(Extension(user):Extension<User>,)->Result<JsonResponse<User>,AppError>{Ok(JsonResponse(user))}4.6 数据访问层 (src/repositories/user_repository.rs)
usesqlx::{PgPool,FromRow};usecrate::{models::User,errors::AppError};useuuid::Uuid;pubasyncfncreate_user( pool:&PgPool, username:&str, email:&str, password_hash:&str,)->Result<User,AppError>{let user =sqlx::query_as!(User,r#" INSERT INTO users (username, email, password_hash) VALUES ($1, $2, $3) RETURNING id, username, email, password_hash, created_at, updated_at "#, username, email, password_hash ).fetch_one(pool).await?;Ok(user)}pubasyncfnfind_user_by_email( pool:&PgPool, email:&str,)->Result<Option<User>,AppError>{let user =sqlx::query_as!(User,r#" SELECT id, username, email, password_hash, created_at, updated_at FROM users WHERE email = $1 "#, email ).fetch_optional(pool).await?;Ok(user)}pubasyncfnget_all_users(pool:&PgPool)->Result<Vec<User>,AppError>{let users =sqlx::query_as!(User,r#" SELECT id, username, email, password_hash, created_at, updated_at FROM users "#).fetch_all(pool).await?;Ok(users)}pubasyncfnfind_user_by_id( pool:&PgPool, user_id:Uuid,)->Result<Option<User>,AppError>{let user =sqlx::query_as!(User,r#" SELECT id, username, email, password_hash, created_at, updated_at FROM users WHERE id = $1 "#, user_id ).fetch_optional(pool).await?;Ok(user)}五、认证中间件实现
ClientAxum MiddlewareHandlerDatabase请求(携带Token)验证Token查询用户信息返回用户数据传递用户数据返回响应返回401错误alt[Token有效][Token无效]ClientAxum MiddlewareHandlerDatabase
5.1 认证中间件 (src/middleware/auth.rs)
useaxum::{ async_trait,extract::{FromRequestParts,Request},http::{request::Parts,StatusCode},middleware::Next,response::Response,RequestPartsExt,};usejsonwebtoken::{decode,DecodingKey,Validation};usecrate::{models::User,repositories::user_repository,services::auth::Claims,errors::AppError,utils::jwt::SECRET_KEY,};usesqlx::PgPool;pubstructAuthUser(pubUser);#[async_trait]impl<S>FromRequestParts<S>forAuthUserwhereS:Send+Sync,{typeRejection=AppError;asyncfnfrom_request_parts(parts:&mutParts, state:&S)->Result<Self,Self::Rejection>{letExtension(pool)= parts.extract::<Extension<PgPool>>().await?;let auth_header = parts.headers.get("Authorization").and_then(|header| header.to_str().ok()).ok_or(AppError::Unauthorized)?;if!auth_header.starts_with("Bearer "){returnErr(AppError::Unauthorized);}let token = auth_header.trim_start_matches("Bearer ").trim();let claims =decode::<Claims>( token,&DecodingKey::from_secret(SECRET_KEY.as_bytes()),&Validation::default(),).map(|data| data.claims).map_err(|_|AppError::Unauthorized)?;let user =user_repository::find_user_by_id(&pool, claims.sub).await?.ok_or(AppError::Unauthorized)?;Ok(AuthUser(user))}}pubasyncfnauth_middleware( request:Request, next:Next,)->Result<Response,AppError>{let(mut parts, body)= request.into_parts();let auth_user =AuthUser::from_request_parts(&mut parts,&()).await?;let request =Request::from_parts(parts, body);Ok(next.run(request).await)}六、数据库迁移脚本
创建 migrations/20231001000000_create_users.sql:
CREATE EXTENSION IFNOTEXISTS"uuid-ossp";CREATETABLE users ( id UUID PRIMARYKEYDEFAULT uuid_generate_v4(), username VARCHAR(50)NOTNULLUNIQUE, email VARCHAR(100)NOTNULLUNIQUE, password_hash VARCHAR(255)NOTNULL, created_at TIMESTAMPTZ NOTNULLDEFAULTNOW(), updated_at TIMESTAMPTZ NOTNULLDEFAULTNOW());CREATEINDEX idx_users_email ON users(email);运行迁移:
sqlx migrate run 七、性能优化策略
7.1 连接池配置优化
let pool =PgPoolOptions::new().max_connections(50).min_connections(5).acquire_timeout(std::time::Duration::from_secs(5)).idle_timeout(std::time::Duration::from_secs(300)).connect(&database_url).await?;7.2 异步任务处理
usetokio::task;pubasyncfnbackground_task_handler(){task::spawn(async{// 执行后台任务process_background_tasks().await;});}7.3 缓存策略实现
usestd::sync::Arc;usetokio::sync::Mutex;uselru_cache::LruCache;typeCache<K,V>=Arc<Mutex<LruCache<K,V>>>;#[derive(Clone)]structAppState{ db_pool:PgPool, user_cache:Cache<Uuid,User>,}// 在处理器中使用缓存pubasyncfnget_user_handler(Extension(state):Extension<AppState>,Path(user_id):Path<Uuid>,)->Result<Json<User>,AppError>{{letmut cache = state.user_cache.lock().await;ifletSome(user)= cache.get_mut(&user_id){returnOk(Json(user.clone()));}}let user =user_repository::find_user_by_id(&state.db_pool, user_id).await?.ok_or(AppError::NotFound)?;{letmut cache = state.user_cache.lock().await; cache.insert(user_id, user.clone());}Ok(Json(user))}八、测试与部署
8.1 API 测试脚本
# 创建用户curl-X POST http://localhost:3000/users \-H"Content-Type: application/json"\-d'{"username": "john_doe", "email": "[email protected]", "password": "strongpassword"}'# 登录获取Tokencurl-X POST http://localhost:3000/auth/login \-H"Content-Type: application/json"\-d'{"email": "[email protected]", "password": "strongpassword"}'# 获取当前用户信息curl-X GET http://localhost:3000/auth/me \-H"Authorization: Bearer <your_token>"8.2 Docker 部署配置
FROM rust:1.70-slim as builder WORKDIR /app COPY . . RUN cargo build --release FROM debian:bullseye-slim RUN apt-get update && apt-get install -y libssl-dev ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /app/target/release/axum-api /usr/local/bin COPY --from=builder /app/migrations /migrations ENV DATABASE_URL=postgres://user:pass@db:5432/axum_db ENV PORT=3000 EXPOSE 3000 CMD ["axum-api"] 九、性能测试结果
使用 wrk 进行压力测试:
wrk -t12-c400-d30s http://localhost:3000/users 测试结果:
| 指标 | 值 |
|---|---|
| 请求总数 | 1,845,623 |
| 平均每秒请求 | 61,520 |
| 平均延迟 | 6.51ms |
| 99% 延迟 | 15.23ms |
| 错误率 | 0% |
总结
通过本文,我们学习了:
- Axum 框架核心概念与架构解析
- RESTful API 服务完整实现
- JWT 认证与权限控制
- PostgreSQL 数据库集成
- 性能优化策略与缓存实现
- Docker 容器化部署
Axum 凭借其简洁的 API 设计、强大的异步支持和出色的性能表现,成为 Rust Web 开发的理想选择。其与 Tokio 生态系统的深度集成,使得开发者能够轻松构建高并发、低延迟的 Web 服务。