使用 Actix-Web、SQLx 和 Redis 构建高性能 Rust Web 服务:模块化结构

使用 Actix-Web、SQLx 和 Redis 构建高性能 Rust Web 服务:模块化结构

在本教程中,我们将详细介绍如何在 Windows 系统上使用 Actix-Web 框架、SQLx 数据库工具和 Redis 缓存来构建一个高性能的 Rust Web 服务。我们将创建一个用户管理系统,包括获取单个用户、创建用户和获取所有用户的功能,并通过模块化结构来组织代码,使其更加清晰和易于维护。

什么是 Actix-Web

Actix-Web 是一个用于构建 Web 应用程序的高性能、异步框架,基于 Rust 语言。它具有以下特点:

  • 异步 :使用 tokio 和 async-std 等异步运行时,提供高性能和高并发处理能力。
  • 中间件:支持中间件机制,可以方便地扩展功能。
  • 路由:灵活的路由机制,支持路径参数、查询参数等多种路由方式。
  • 类型安全:利用 Rust 的类型系统,确保代码的类型安全性和健壮性。
  • 社区支持:拥有活跃的开发者社区和丰富的文档资源。

准备工作

安装 Rust 和 Cargo

首先,确保你已经安装了 Rust 编程语言和 Cargo 包管理器。你可以在 Rust 官网 上找到适用于 Windows 的安装指南。按照提示运行 Rust 安装程序,并在安装过程中选择安装 Visual C++ 构建工具。

安装 MySQL

安装 MySQL 数据库,并设置好对应的环境变量。你可以从 MySQL 官网 下载适用于 Windows 的安装程序,并按照官方指南进行安装。

安装 Redis

安装 Redis,并设置好对应的环境变量。你可以从 Redis 官网 下载 Windows 版本的安装程序,并按照官方指南进行安装。

创建项目

首先,使用 Cargo 创建一个新的 Rust 项目:

sh 复制代码
cargo new my_actix_app
cd my_actix_app

添加依赖项

编辑 Cargo.toml 文件,添加所需的依赖项:

toml 复制代码
[package]
name = "my_actix_app"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.0"
sqlx = { version = "0.5", features = ["mysql", "runtime-tokio-native-tls"] }
redis = "0.26"
dotenv = "0.15"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

创建目录结构

为了更好地组织代码,我们可以创建一个 models 目录来存放数据模型,以及一个 db 目录来存放 SQL 查询逻辑。创建以下目录结构:

复制代码
my_actix_app/
├── src/
│   ├── main.rs
│   ├── models/
│   │   └── user.rs
│   └── db/
│       └── user_queries.rs
├── .env
└── Cargo.toml

创建数据模型

在 src/models/user.rs 文件中创建用户数据模型:

rust 复制代码
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
}

创建 SQL 查询逻辑

在 src/db/user_queries.rs 文件中创建用户相关的 SQL 查询逻辑:

rust 复制代码
use sqlx::mysql::MySqlPool;
use crate::models::User;
use sqlx::Error;

// 获取单个用户
pub async fn get_user_by_id(pool: &MySqlPool, user_id: i32) -> Result<Option<User>, Error> {
    sqlx::query_as!(
        User,
        r#"
        SELECT id, name, email FROM users WHERE id = ?
        "#,
        user_id
    )
    .fetch_optional(pool)
    .await
}

// 创建新用户
pub async fn create_user(pool: &MySqlPool, name: String, email: String) -> Result<User, Error> {
    let result = sqlx::query!(
        r#"
        INSERT INTO users (name, email) VALUES (?, ?)
        "#,
        name,
        email
    )
    .execute(pool)
    .await?;

    Ok(User {
        id: result.last_insert_id() as i32,
        name,
        email,
    })
}

// 获取所有用户
pub async fn get_all_users(pool: &MySqlPool) -> Result<Vec<User>, Error> {
    sqlx::query_as!(
        User,
        r#"
        SELECT id, name, email FROM users
        "#
    )
    .fetch_all(pool)
    .await
}

编写代码

在 src/main.rs 文件中编写代码。以下是完整的代码示例,并在关键部分添加了注解:

rust 复制代码
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use sqlx::mysql::MySqlPool;
use redis::Client as RedisClient;
use std::sync::Arc;
use dotenv::dotenv;
use serde_json::json;
use std::env;

// 引入 models 目录中的 User 结构体
mod models;
use models::User;

// 引入 db 目录中的用户查询逻辑
mod db;
use db::user_queries::{get_user_by_id, create_user, get_all_users};

// 定义 AppState 结构体,用于存储数据库连接和 Redis 客户端
struct AppState {
    db: MySqlPool,
    redis: RedisClient,
}

// 获取单个用户
#[get("/users/{id}")]
async fn get_user(path: web::Path<i32>, data: web::Data<AppState>) -> impl Responder {
    let user_id = path.into_inner();
    let pool = &data.db;

    // 查询用户
    let query_result = get_user_by_id(pool, user_id).await;

    match query_result {
        Ok(Some(user)) => HttpResponse::Ok().json(user),
        Ok(None) => HttpResponse::NotFound().body("用户未找到"),
        Err(_) => HttpResponse::InternalServerError().body("获取用户时出错"),
    }
}

// 创建新用户
#[post("/users")]
async fn create_user_handler(user: web::Json<User>, data: web::Data<AppState>) -> impl Responder {
    let pool = &data.db;

    // 插入用户
    let insert_result = create_user(pool, user.name.clone(), user.email.clone()).await;

    match insert_result {
        Ok(user) => HttpResponse::Created().json(user),
        Err(_) => HttpResponse::InternalServerError().body("创建用户时出错"),
    }
}

// 获取所有用户
#[get("/users")]
async fn get_all_users_handler(data: web::Data<AppState>) -> impl Responder {
    let pool = &data.db;

    // 查询所有用户
    let query_result = get_all_users(pool).await;

    match query_result {
        Ok(users) => HttpResponse::Ok().json(users),
        Err(_) => HttpResponse::InternalServerError().body("获取用户时出错"),
    }
}

// 主函数,设置数据库连接和 Redis 客户端,并启动 HTTP 服务器
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    // 从环境变量中读取数据库 URL
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL 必须设置");
    // 创建数据库连接池
    let pool = MySqlPool::connect(&database_url).await.expect("创建连接池失败");

    // 从环境变量中读取 Redis URL
    let redis_url = env::var("REDIS_URL").expect("REDIS_URL 必须设置");
    // 创建 Redis 客户端
    let redis_client = RedisClient::open(redis_url).expect("创建 Redis 客户端失败");

    println!("服务器运行在 http://127.0.0.1:8880");

    // 启动 HTTP 服务器
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(AppState {
                db: pool.clone(),
                redis: redis_client.clone(),
            }))
            .service(get_user)
            .service(create_user_handler)
            .service(get_all_users_handler)
    })
    .bind(("127.0.0.1", 8880))?
    .run()
    .await
}

创建数据库表

在 MySQL 数据库中创建一个名为 users 的表,包括 id、name 和 email 字段。你可以使用以下 SQL 语句来创建表:

sql 复制代码
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

设置环境变量

在项目的根目录下创建一个 .env 文件,并添加以下内容:

复制代码
DATABASE_URL=mysql://username:password@localhost/database_name
REDIS_URL=redis://127.0.0.1/

请将 username、password 和 database_name 替换为你的 MySQL 数据库的实际值。

运行项目

最后,使用 Cargo 运行项目,并访问 http://127.0.0.1:8880 来测试 API 接口的功能。

sh 复制代码
cargo run

通过本教程,你将学会如何在 Windows 系统上使用 Actix-Web、SQLx 和 Redis 构建一个高性能的 Rust Web 服务,并且通过合理的项目结构(如使用 models 目录存放数据模型,db 目录存放 SQL 查询逻辑)来组织代码,实现了用户管理系统的基本功能。

相关推荐
定栓9 分钟前
Webpack迁到Vite第一节:模块系统迁移:require → import
运维·前端·webpack
小熊在忙fpga18 分钟前
N32G430芯片的flash表示
前端·javascript·html
Data analyse45624 分钟前
App埋点版本兼容的字段迁移校验
大数据·运维·服务器·前端·数据库·数据分析
todoitbo30 分钟前
本来只想给内网开个口子,Natapp 却多给了一道防护
前端·javascript·vue.js·内网穿透·网络防护·natapp
会灭火的程序员35 分钟前
SuperMap AgentX Server智能体接入WEB应用技术文档
前端·supermap·agentx·agentx server
CRMEB40 分钟前
商品 SKU 设计,从简单规格到多规格踩过什么
java·前端·数据库·开源
Chloe.Zz41 分钟前
一篇入门Agent
开发语言·前端·javascript
hweiyu0044 分钟前
Redis命令:RPOPLPUSH
redis·缓存
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 表格标签
前端·html·html5
雪芽蓝域zzs1 小时前
JS 项目转 TS 项目(Vue3 + 若依这类后台项目)完整改造清单
前端·vue.js