【Axum】Rust Web 高效构建:Axum 框架从入门到精通指南

【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%

总结

通过本文,我们学习了:

  1. Axum 框架核心概念与架构解析
  2. RESTful API 服务完整实现
  3. JWT 认证与权限控制
  4. PostgreSQL 数据库集成
  5. 性能优化策略与缓存实现
  6. Docker 容器化部署

Axum 凭借其简洁的 API 设计、强大的异步支持和出色的性能表现,成为 Rust Web 开发的理想选择。其与 Tokio 生态系统的深度集成,使得开发者能够轻松构建高并发、低延迟的 Web 服务。

Read more

使用 VS Code 连接 MySQL 数据库

使用 VS Code 连接 MySQL 数据库

文章目录 * 前言 * VS Code下载安装 * 如何在VS Code上连接MySQL数据库 * 1、打开扩展 * 2、安装MySQL插件 * 3、连接 * 导入和导出表结构和数据 前言 提示:这里可以添加本文要记录的大概内容: 听说VS Code不要钱,功能还和 Navicat 差不多,还能在上面打游戏 但是没安装插件是不行的 发现一个非常牛的博主 还有一个非常牛的大佬 提示:以下是本篇文章正文内容,下面案例可供参考 VS Code下载安装 VS Code下载安装 如何在VS Code上连接MySQL数据库 本篇分享是在已有VS Code这个软件的基础上,数据库举的例子是MySQL 1、打开扩展 2、安装MySQL插件 在搜索框搜索 MySQL和 MySQL Syntax,下载这三个插件 点击下面的插件,选择【install】安装

By
RustFS 保姆级上手指南:国产开源高性能对象存储

RustFS 保姆级上手指南:国产开源高性能对象存储

最近在给项目选型对象存储的时候,发现一个挺有意思的现象:一边是MinIO社区版功能逐渐“躺平”,另一边是大家对存储性能和安全性的要求越来越高。就在这时,一个叫 RustFS 的国产开源项目闯入了我的视野。 折腾了一阵子后,我感觉这玩意儿确实有点东西。它用Rust语言写,天生就带着高性能和内存安全的基因,性能号称比MinIO快一大截,而且用的是对商业友好的Apache 2.0协议。今天,我就手把手带大家从零开始,搭建一个属于自己的RustFS服务,体验一下国产存储的威力。 一、 RustFS是什么?为什么值得你关注? 简单说,RustFS是一个 分布式对象存储系统 。你可以把它理解成一个你自己搭建的、功能跟阿里云OSS、亚马逊S3几乎一样的“私有云盘”。 但它有几个非常突出的亮点,让我觉得必须试试: * 性能猛兽 :基于Rust语言开发,没有GC(垃圾回收)带来的性能抖动,官方数据显示在4K随机读场景下,性能比MinIO高出40%以上,内存占用还不到100MB,简直是“小钢炮”。 * 100%S3兼容 :这意味着你现有的所有使用S3 API的代码、工具(比如AWS

By