从零到一:用 Rust 和 Actix-web 构建高性能 REST API

3 阅读1分钟

在当今的微服务架构和云原生应用中,高性能的 API 服务是系统成功的关键。虽然 Node.js、Python 等语言在 API 开发中占据主流地位,但 Rust 凭借其卓越的性能和内存安全性,正在成为构建关键基础设施的新选择。本文将带你使用 Rust 和 Actix-web 框架,从零开始构建一个完整的高性能 REST API。

为什么选择 Rust 和 Actix-web?

Rust 的优势

  • 零成本抽象:高级特性不会带来运行时开销
  • 内存安全:编译时保证内存安全,无需垃圾回收
  • 并发安全:所有权系统防止数据竞争
  • 卓越性能:与 C/C++ 相媲美的运行速度

Actix-web 的特点

  • Actor 模型:基于 Actor 的并发模型
  • 类型安全:强大的类型系统
  • 中间件支持:灵活的中间件生态系统
  • 异步支持:完整的 async/await 支持

项目搭建

1. 创建项目

cargo new rust-api-demo
cd rust-api-demo

2. 添加依赖

修改 Cargo.toml

[package]
name = "rust-api-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1.0", features = ["v4", "serde"] }
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] }
dotenv = "0.15"

核心架构设计

分层架构

├── src/
│   ├── main.rs          # 应用入口
│   ├── models/          # 数据模型
│   ├── handlers/        # 请求处理器
│   ├── routes/          # 路由定义
│   ├── database/        # 数据库连接
│   └── middleware/      # 中间件

完整实现

1. 定义数据模型

// src/models/user.rs
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct User {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Deserialize)]
pub struct CreateUser {
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Debug, Deserialize)]
pub struct UpdateUser {
    pub username: Option<String>,
    pub email: Option<String>,
}

// 实现 From trait 用于类型转换
impl From<CreateUser> for User {
    fn from(user: CreateUser) -> Self {
        let now = Utc::now();
        User {
            id: Uuid::new_v4(),
            username: user.username,
            email: user.email,
            created_at: now,
            updated_at: now,
        }
    }
}

2. 数据库连接池

// src/database/mod.rs
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::env;

pub type DbPool = PgPool;

pub async fn create_pool() -> Result<DbPool, sqlx::Error> {
    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    
    PgPoolOptions::new()
        .max_connections(10)
        .connect(&database_url)
        .await
}

3. 请求处理器

// src/handlers/user_handler.rs
use actix_web::{web, HttpResponse, Responder};
use crate::models::user::{User, CreateUser, UpdateUser};
use crate::database::DbPool;
use uuid::Uuid;

// 创建用户
pub async fn create_user(
    pool: web::Data<DbPool>,
    user_data: web::Json<CreateUser>,
) -> impl Responder {
    let user: User = user_data.into_inner().into();
    
    let result = sqlx::query!(
        r#"
        INSERT INTO users (id, username, email, created_at, updated_at)
        VALUES ($1, $2, $3, $4, $5)
        RETURNING id, username, email, created_at, updated_at
        "#,
        user.id,
        user.username,
        user.email,
        user.created_at,
        user.updated_at
    )
    .fetch_one(pool.get_ref())
    .await;
    
    match result {
        Ok(record) => {
            let created_user = User {
                id: record.id,
                username: record.username,
                email: record.email,
                created_at: record.created_at,
                updated_at: record.updated_at,
            };
            HttpResponse::Created().json(created_user)
        }
        Err(e) => {
            eprintln!("Failed to create user: {}", e);
            HttpResponse::InternalServerError().body("Failed to create user")
        }
    }
}

// 获取用户列表(带分页)
pub async fn get_users(
    pool: web::Data<DbPool>,
    query: web::Query<PaginationQuery>,
) -> impl Responder {
    let page = query.page.unwrap_or(1);
    let per_page = query.per_page.unwrap_or(10);
    let offset = (page - 1) * per_page;
    
    let users = sqlx::query_as!(
        User,
        r#"
        SELECT id, username, email, created_at, updated_at
        FROM users
        ORDER BY created_at DESC
        LIMIT $1 OFFSET $2
        "#,
        per_page as i64,
        offset as i64
    )
    .fetch_all(pool.get_ref())
    .await;
    
    match users {
        Ok(users) => HttpResponse::Ok().json(users),
        Err(e) => {
            eprintln!("Failed to fetch users: {}", e);
            HttpResponse::InternalServerError().body("Failed to fetch users")
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct PaginationQuery {
    pub page: Option<u32>,
    pub per_page: Option<u32>,
}

4. 自定义中间件

// src/middleware/logger.rs
use actix_web::{
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use futures::future::{ok, Ready};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
    time::Instant,
};

pub struct Logger;

impl<S, B> Transform<S, ServiceRequest> for Logger
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = LoggerMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(LoggerMiddleware { service })
    }
}

pub struct LoggerMiddleware<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for LoggerMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let start_time = Instant::now();
        let method = req.method().clone();
        let path = req.path().to_string();
        
        let fut = self.service.call(req);
        
        Box::pin(async move {
            let res = fut.await?;
            let duration = start_time.elapsed();
            
            println!(
                "{} {} - {}ms - Status: {}",
                method,
                path,
                duration.as_millis(),
                res.status()
            );
            
            Ok(res)
        })
    }
}

5. 路由配置

// src/routes/mod.rs
use actix_web::web;
use crate::handlers::user_handler