【Actix Web 精要】Rust Web 服務開發核心技術與實戰指南

目錄

    • 一、Actix Web 核心架構解析
      • 1.1 核心組件交互流程
      • 1.2 關鍵組件說明:
    • 二、項目初始化與配置
      • 2.1 創建項目
      • 2.2 添加依賴 (Cargo.toml)
      • 2.3 項目結構
    • 三、核心模塊實現
      • 3.1 配置管理 (src/config.rs)
      • 3.2 應用狀態管理 (src/main.rs)
      • 3.3 數據模型 (src/models/user.rs)
    • 四、路由與請求處理
      • 4.1 路由配置 (src/routes.rs)
      • 4.2 用戶認證處理器 (src/handlers/auth.rs)
      • 4.3 用戶管理處理器 (src/handlers/user.rs)
    • 五、服務層與數據訪問
      • 5.1 認證服務 (src/services/auth.rs)
      • 5.2 用戶服務 (src/services/user.rs)
      • 5.3 數據訪問層 (src/repositories/user.rs)
    • 六、認證與授權
      • 6.1 JWT 認證中間件
      • 6.2 認證中間件實現 (src/services/auth.rs)
      • 6.3 JWT 工具函數 (src/utils/jwt.rs)
    • 七、錯誤處理
      • 7.1 統一錯誤處理 (src/errors.rs)
    • 八、數據庫遷移
      • 8.1 遷移腳本 (migrations/20231001000000_create_users.sql)
      • 8.2 運行遷移
    • 九、性能優化策略
      • 9.1 數據庫連接池優化
      • 9.2 Redis 緩存集成
      • 9.3 異步任務處理
    • 十、測試與部署
      • 10.1 集成測試示例
      • 10.2 Docker 生產部署
      • 10.3 Kubernetes 部署配置
    • 十一、性能測試結果
    • 總結

Actix Web 是 Rust 生態中最強大的 Web 框架之一,以其卓越的性能和安全性著稱。本文將深入探討 Actix Web 的核心技術,通過構建一個完整的用戶管理系統,展示如何高效開發生產級 Rust Web 應用。

一、Actix Web 核心架構解析

1.1 核心組件交互流程

客戶端請求
HttpServer
App 實例
路由系統
中間件鏈
請求處理器
應用狀態
數據庫/服務

1.2 關鍵組件說明:

  • HttpServer:管理HTTP服務器和工作者線程
  • App:應用實例,包含路由和共享狀態
  • 路由系統:將URL映射到處理器函數
  • 中間件鏈:處理請求/響應的預處理和后處理
  • 應用狀態:線程安全的共享數據(數據庫連接池等)
  • 請求處理器:異步函數處理具體業務邏輯

二、項目初始化與配置

2.1 創建項目

cargo new actix-essentials
cd actix-essentials

2.2 添加依賴 (Cargo.toml)

[package]
name = "actix-essentials"
version = "0.1.0"
edition = "2021"[dependencies]
actix-web = "4.4.0"
actix-rt = "2.2.0"        # Actix 運行時
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.7.1", features = ["postgres", "runtime-tokio", "macros"] 
}
dotenv = "0.15.0"         # 環境變量管理
config = "0.13.0"         # 配置管理
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
uuid = { version = "1.3.0", features = ["v4", "serde"] }
bcrypt = "0.15.0"         # 密碼哈希
jsonwebtoken = "9.1.0"    # JWT 認證
validator = { version = "0.16.0", features = ["derive"] } # 數據驗證
tracing = "0.1.37"        # 結構化日志
tracing-subscriber = "0.3.17"
tracing-actix-web = "0.7.0" # Actix Web 集成

2.3 項目結構

src/
├── main.rs         # 應用入口
├── config.rs       # 配置管理
├── routes.rs       # 路由配置
├── handlers/       # 請求處理器
│   ├── mod.rs
│   ├── auth.rs
│   ├── user.rs
├── models/         # 數據模型
│   ├── mod.rs
│   ├── user.rs
├── services/       # 業務邏輯
│   ├── mod.rs
│   ├── auth.rs
│   ├── user.rs
├── repositories/   # 數據訪問
│   ├── mod.rs
│   ├── user.rs
├── errors.rs       # 錯誤處理
├── utils/          # 工具函數
│   ├── mod.rs
│   ├── jwt.rs
│   ├── cache.rs

三、核心模塊實現

3.1 配置管理 (src/config.rs)

use config::{Config as ConfigBuilder, Environment, File};
use serde::Deserialize;
use std::env;#[derive(Debug, Clone, Deserialize)]
pub struct ServerConfig {pub host: String,pub port: u16,pub workers: Option<usize>,
}#[derive(Debug, Clone, Deserialize)]
pub struct DatabaseConfig {pub url: String,pub max_connections: u32,pub min_connections: u32,
}#[derive(Debug, Clone, Deserialize)]
pub struct AuthConfig {pub secret_key: String,pub token_expiration: i64,
}#[derive(Debug, Clone, Deserialize)]
pub struct AppConfig {pub server: ServerConfig,pub database: DatabaseConfig,pub auth: AuthConfig,
}impl AppConfig {pub fn load() -> Result<Self, config::ConfigError> {let env = env::var("APP_ENV").unwrap_or_else(|_| "development".into());ConfigBuilder::builder()// 添加默認配置文件.add_source(File::with_name("config/default").required(false))// 添加環境特定配置文件.add_source(File::with_name(&format!("config/{}", env)).required(false))// 添加環境變量 (APP_前綴).add_source(Environment::with_prefix("APP")).build()?.try_deserialize()}
}

3.2 應用狀態管理 (src/main.rs)

use actix_web::{web, App, HttpServer};
use sqlx::postgres::PgPoolOptions;
use crate::config::AppConfig;pub struct AppState {pub db_pool: sqlx::PgPool,pub config: AppConfig,
}#[actix_web::main]
async fn main() -> std::io::Result<()> {// 初始化日志tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();// 加載配置let config = AppConfig::load().expect("Failed to load configuration");// 創建數據庫連接池let db_pool = PgPoolOptions::new().max_connections(config.database.max_connections).min_connections(config.database.min_connections).connect(&config.database.url).await.expect("Failed to create database pool");// 運行數據庫遷移sqlx::migrate!().run(&db_pool).await.expect("Migration failed");// 創建應用狀態let app_state = web::Data::new(AppState {db_pool,config: config.clone(),});// 啟動 HTTP 服務器let server = HttpServer::new(move || {App::new().app_data(app_state.clone()).configure(crate::routes::config).wrap(tracing_actix_web::TracingLogger::default())}).bind((config.server.host.as_str(), config.server.port))?.workers(config.server.workers.unwrap_or(num_cpus::get()));tracing::info!("Starting server at {}:{}", config.server.host, config.server.port);server.run().await
}

3.3 數據模型 (src/models/user.rs)

use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
use validator::Validate;
use chrono::{DateTime, Utc};#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct User {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, Validate)]
pub struct CreateUser {#[validate(length(min = 3, max = 50))]pub username: String,#[validate(email)]pub email: String,#[validate(length(min = 8))]pub password: String,
}#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUser {#[validate(length(min = 3, max = 50))]pub username: Option<String>,#[validate(email)]pub email: Option<String>,#[validate(length(min = 8))]pub password: Option<String>,
}#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {#[validate(email)]pub email: String,#[validate(length(min = 8))]pub password: String,
}#[derive(Debug, Serialize)]
pub struct AuthResponse {pub token: String,pub user: User,
}

四、路由與請求處理

4.1 路由配置 (src/routes.rs)

use actix_web::web;pub fn config(cfg: &mut web::ServiceConfig) {cfg.service(web::scope("/api").service(web::scope("/auth").route("/register", web::post().to(crate::handlers::auth::register)).route("/login", web::post().to(crate::handlers::auth::login)).route("/me", web::get().to(crate::handlers::auth::current_user))).service(web::scope("/users").route("", web::get().to(crate::handlers::user::list_users)).route("/{id}", web::get().to(crate::handlers::user::get_user)).route("/{id}", web::put().to(crate::handlers::user::update_user)).route("/{id}", web::delete().to(crate::handlers::user::delete_user))));
}

4.2 用戶認證處理器 (src/handlers/auth.rs)

use actix_web::{web, HttpResponse};
use crate::{models::{CreateUser, LoginRequest, AuthResponse}, services::auth, errors::AppError, AppState};pub async fn register(state: web::Data<AppState>,form: web::Json<CreateUser>,
) -> Result<HttpResponse, AppError> {// 驗證輸入form.validate().map_err(AppError::Validation)?;// 創建用戶let user = auth::register_user(&state.db_pool, &form).await?;// 生成JWTlet token = auth::create_jwt(&state.config.auth, user.id)?;Ok(HttpResponse::Created().json(AuthResponse { token, user }))
}pub async fn login(state: web::Data<AppState>,form: web::Json<LoginRequest>,
) -> Result<HttpResponse, AppError> {// 驗證輸入form.validate().map_err(AppError::Validation)?;// 用戶認證let user = auth::authenticate(&state.db_pool, &form.email, &form.password).await?;// 生成JWTlet token = auth::create_jwt(&state.config.auth, user.id)?;Ok(HttpResponse::Ok().json(AuthResponse { token, user }))
}pub async fn current_user(user: auth::AuthenticatedUser,
) -> Result<HttpResponse, AppError> {Ok(HttpResponse::Ok().json(&user.0))
}

4.3 用戶管理處理器 (src/handlers/user.rs)

use actix_web::{web, HttpResponse};
use crate::{models::UpdateUser, services::user, errors::AppError, AppState};
use uuid::Uuid;pub async fn list_users(state: web::Data<AppState>,
) -> Result<HttpResponse, AppError> {let users = user::list_users(&state.db_pool).await?;Ok(HttpResponse::Ok().json(users))
}pub async fn get_user(state: web::Data<AppState>,user_id: web::Path<Uuid>,
) -> Result<HttpResponse, AppError> {let user = user::get_user(&state.db_pool, *user_id).await?;Ok(HttpResponse::Ok().json(user))
}pub async fn update_user(state: web::Data<AppState>,user_id: web::Path<Uuid>,form: web::Json<UpdateUser>,
) -> Result<HttpResponse, AppError> {// 驗證輸入if let Some(ref username) = form.username {if username.len() < 3 || username.len() > 50 {return Err(AppError::Validation("Invalid username".into()));}}// 更新用戶let updated_user = user::update_user(&state.db_pool, *user_id, &form).await?;Ok(HttpResponse::Ok().json(updated_user))
}pub async fn delete_user(state: web::Data<AppState>,user_id: web::Path<Uuid>,
) -> Result<HttpResponse, AppError> {user::delete_user(&state.db_pool, *user_id).await?;Ok(HttpResponse::NoContent().finish()))
}

五、服務層與數據訪問

5.1 認證服務 (src/services/auth.rs)

use crate::{models::{User, CreateUser, LoginRequest}, repositories::user as user_repo, AppError, utils::jwt};
use bcrypt::{hash, verify, DEFAULT_COST};
use uuid::Uuid;pub async fn register_user(pool: &sqlx::PgPool,form: &CreateUser,
) -> Result<User, AppError> {// 檢查郵箱是否已存在if user_repo::email_exists(pool, &form.email).await? {return Err(AppError::Conflict("Email already exists".into()));}// 哈希密碼let hashed_password = hash_password(&form.password)?;// 創建用戶let user = user_repo::create_user(pool, &form.username, &form.email, &hashed_password).await?;Ok(user)
}pub async fn authenticate(pool: &sqlx::PgPool,email: &str,password: &str,
) -> Result<User, AppError> {// 獲取用戶let user = user_repo::find_by_email(pool, email).await?.ok_or(AppError::Unauthorized("Invalid credentials".into()))?;// 驗證密碼if !verify_password(password, &user.password_hash)? {return Err(AppError::Unauthorized("Invalid credentials".into()));}Ok(user)
}pub fn create_jwt(auth_config: &crate::config::AuthConfig,user_id: Uuid,
) -> Result<String, AppError> {jwt::create_token(&auth_config.secret_key, user_id, auth_config.token_expiration)
}fn hash_password(password: &str) -> Result<String, AppError> {hash(password, DEFAULT_COST).map_err(|_| AppError::Internal("Failed to hash password".into()))
}fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {verify(password, hash).map_err(|_| AppError::Internal("Password verification failed".into()))
}

5.2 用戶服務 (src/services/user.rs)

use crate::{models::User, repositories::user as user_repo, AppError};
use uuid::Uuid;pub async fn list_users(pool: &sqlx::PgPool,
) -> Result<Vec<User>, AppError> {user_repo::list_users(pool).await
}pub async fn get_user(pool: &sqlx::PgPool,user_id: Uuid,
) -> Result<User, AppError> {user_repo::find_by_id(pool, user_id).await?.ok_or(AppError::NotFound("User not found".into()))
}pub async fn update_user(pool: &sqlx::PgPool,user_id: Uuid,form: &crate::models::UpdateUser,
) -> Result<User, AppError> {let mut user = user_repo::find_by_id(pool, user_id).await?.ok_or(AppError::NotFound("User not found".into()))?;// 更新字段if let Some(username) = &form.username {user.username = username.clone();}if let Some(email) = &form.email {user.email = email.clone();}if let Some(password) = &form.password {user.password_hash = hash_password(password)?;}// 保存更新user_repo::update_user(pool, &user).await
}pub async fn delete_user(pool: &sqlx::PgPool,user_id: Uuid,
) -> Result<(), AppError> {user_repo::delete_user(pool, user_id).await
}fn hash_password(password: &str) -> Result<String, AppError> {bcrypt::hash(password, DEFAULT_COST).map_err(|_| AppError::Internal("Failed to hash password".into()))
}

5.3 數據訪問層 (src/repositories/user.rs)

use crate::{models::User, AppError};
use sqlx::PgPool;
use uuid::Uuid;pub async fn create_user(pool: &PgPool,username: &str,email: &str,password_hash: &str,
) -> Result<User, AppError> {sqlx::query_as!(User,r#"INSERT INTO users (username, email, password_hash)VALUES ($1, $2, $3)RETURNING *"#,username,email,password_hash).fetch_one(pool).await.map_err(|e| {if e.as_database_error().and_then(|db_err| db_err.code().map(|c| c == "23505")).unwrap_or(false){AppError::Conflict("Email already exists".into())} else {AppError::Database(e)}})
}pub async fn find_by_id(pool: &PgPool,user_id: Uuid,
) -> Result<Option<User>, AppError> {sqlx::query_as!(User,r#"SELECT * FROM users WHERE id = $1"#,user_id).fetch_optional(pool).await.map_err(AppError::Database)
}pub async fn find_by_email(pool: &PgPool,email: &str,
) -> Result<Option<User>, AppError> {sqlx::query_as!(User,r#"SELECT * FROM users WHERE email = $1"#,email).fetch_optional(pool).await.map_err(AppError::Database)
}pub async fn email_exists(pool: &PgPool,email: &str,
) -> Result<bool, AppError> {sqlx::query!(r#"SELECT EXISTS(SELECT 1 FROM users WHERE email = $1) AS "exists!""#,email).fetch_one(pool).await.map(|r| r.exists).map_err(AppError::Database)
}pub async fn list_users(pool: &PgPool) -> Result<Vec<User>, AppError> {sqlx::query_as!(User,r#"SELECT * FROM users"#).fetch_all(pool).await.map_err(AppError::Database)
}pub async fn update_user(pool: &PgPool,user: &User,
) -> Result<User, AppError> {sqlx::query_as!(User,r#"UPDATE users SETusername = $1,email = $2,password_hash = $3,updated_at = CURRENT_TIMESTAMPWHERE id = $4RETURNING *"#,user.username,user.email,user.password_hash,user.id).fetch_one(pool).await.map_err(AppError::Database)
}pub async fn delete_user(pool: &PgPool,user_id: Uuid,
) -> Result<(), AppError> {sqlx::query!(r#"DELETE FROM users WHERE id = $1"#,user_id).execute(pool).await.map(|_| ()).map_err(AppError::Database)
}

六、認證與授權

6.1 JWT 認證中間件

Client Middleware Handler Database 請求 (包含Authorization頭) 提取Token 驗證Token簽名 提取用戶ID 查詢用戶信息 返回用戶數據 注入用戶對象 執行業務邏輯 返回響應 Client Middleware Handler Database

6.2 認證中間件實現 (src/services/auth.rs)

use actix_web::{dev::Payload, error, Error, FromRequest, HttpMessage, HttpRequest};
use futures_util::future::{ready, Ready};
use crate::{utils::jwt, models::User, repositories::user, AppError, AppState};pub struct AuthenticatedUser(pub User);impl FromRequest for AuthenticatedUser {type Error = Error;type Future = Ready<Result<Self, Self::Error>>;fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {// 獲取應用狀態let state = match req.app_data::<web::Data<AppState>>() {Some(s) => s,None => return ready(Err(error::ErrorInternalServerError("App state not found"))),};// 提取Authorization頭let auth_header = match req.headers().get("Authorization") {Some(h) => h,None => return ready(Err(error::ErrorUnauthorized("Missing Authorization header"))),};// 提取Bearer Tokenlet token = match auth_header.to_str() {Ok(s) if s.starts_with("Bearer ") => &s[7..],_ => return ready(Err(error::ErrorUnauthorized("Invalid Authorization header"))),};// 驗證Tokenlet claims = match jwt::decode_token(&state.config.auth.secret_key, token) {Ok(c) => c,Err(_) => return ready(Err(error::ErrorUnauthorized("Invalid token"))),};// 獲取數據庫連接池let pool = &state.db_pool;let user_id = claims.sub;// 查詢用戶match user::find_by_id(pool, user_id) {Ok(Some(user)) => ready(Ok(AuthenticatedUser(user))),Ok(None) => ready(Err(error::ErrorUnauthorized("User not found"))),Err(_) => ready(Err(error::ErrorInternalServerError("Database error"))),}}
}

6.3 JWT 工具函數 (src/utils/jwt.rs)

use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use chrono::{Duration, Utc};#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {pub sub: Uuid, // 用戶IDpub exp: usize, // 過期時間
}pub fn create_token(secret: &str,user_id: Uuid,expiration_seconds: i64,
) -> Result<String, jsonwebtoken::errors::Error> {let expiration = Utc::now().checked_add_signed(Duration::seconds(expiration_seconds)).expect("valid timestamp").timestamp() as usize;let claims = Claims {sub: user_id,exp: expiration,};encode(&Header::default(),&claims,&EncodingKey::from_secret(secret.as_bytes()),)
}pub fn decode_token(secret: &str,token: &str,
) -> Result<Claims, jsonwebtoken::errors::Error> {decode::<Claims>(token,&DecodingKey::from_secret(secret.as_bytes()),&Validation::new(Algorithm::HS256),).map(|data| data.claims)
}

七、錯誤處理

7.1 統一錯誤處理 (src/errors.rs)

use actix_web::{http::StatusCode, HttpResponse, ResponseError};
use serde::Serialize;
use std::fmt;
use validator::ValidationErrors;#[derive(Debug)]
pub enum AppError {Validation(String),Unauthorized(String),Forbidden(String),NotFound(String),Conflict(String),Database(sqlx::Error),Internal(String),
}#[derive(Serialize)]
struct ErrorResponse {error: String,#[serde(skip_serializing_if = "Option::is_none")]details: Option<Vec<String>>,
}impl fmt::Display for AppError {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {AppError::Validation(msg) => write!(f, "Validation error: {}", msg),AppError::Unauthorized(msg) => write!(f, "Unauthorized: {}", msg),AppError::Forbidden(msg) => write!(f, "Forbidden: {}", msg),AppError::NotFound(msg) => write!(f, "Not found: {}", msg),AppError::Conflict(msg) => write!(f, "Conflict: {}", msg),AppError::Database(e) => write!(f, "Database error: {}", e),AppError::Internal(msg) => write!(f, "Internal error: {}", msg),}}
}impl ResponseError for AppError {fn status_code(&self) -> StatusCode {match self {AppError::Validation(_) => StatusCode::BAD_REQUEST,AppError::Unauthorized(_) => StatusCode::UNAUTHORIZED,AppError::Forbidden(_) => StatusCode::FORBIDDEN,AppError::NotFound(_) => StatusCode::NOT_FOUND,AppError::Conflict(_) => StatusCode::CONFLICT,AppError::Database(_) | AppError::Internal(_) => {StatusCode::INTERNAL_SERVER_ERROR}}}fn error_response(&self) -> HttpResponse {let status = self.status_code();let error = self.to_string();let response = match self {AppError::Validation(err) => {let details = ValidationErrors::from(err.clone()).field_errors().values().flat_map(|errors| errors.iter().map(|e| e.message.clone().unwrap_or_default())).collect();ErrorResponse {error,details: Some(details),}}_ => ErrorResponse {error,details: None,},};HttpResponse::build(status).json(response)}
}impl From<sqlx::Error> for AppError {fn from(e: sqlx::Error) -> Self {AppError::Database(e)}
}impl From<jsonwebtoken::errors::Error> for AppError {fn from(e: jsonwebtoken::errors::Error) -> Self {AppError::Internal(e.to_string())}
}impl From<ValidationErrors> for AppError {fn from(e: ValidationErrors) -> Self {AppError::Validation(e.to_string())}
}

八、數據庫遷移

8.1 遷移腳本 (migrations/20231001000000_create_users.sql)

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";CREATE TABLE users (id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),username VARCHAR(50) NOT NULL,email VARCHAR(100) NOT NULL UNIQUE,password_hash VARCHAR(255) NOT NULL,created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);CREATE INDEX idx_users_email ON users(email);

8.2 運行遷移

sqlx migrate run

九、性能優化策略

9.1 數據庫連接池優化

// 在配置中設置
let db_pool = PgPoolOptions::new().max_connections(config.database.max_connections).min_connections(config.database.min_connections).acquire_timeout(std::time::Duration::from_secs(5)).idle_timeout(std::time::Duration::from_secs(300)).connect(&config.database.url).await?;

9.2 Redis 緩存集成

// 緩存用戶數據
pub async fn get_user_cached(state: &AppState,user_id: Uuid,
) -> Result<User, AppError> {let cache_key = format!("user:{}", user_id);// 嘗試從緩存獲取if let Some(user) = utils::cache::get::<User>(&state.redis, &cache_key).await? {return Ok(user);}// 數據庫查詢let user = repositories::user::find_by_id(&state.db_pool, user_id).await?.ok_or(AppError::NotFound("User not found".into()))?;// 存儲到緩存utils::cache::set(&state.redis, &cache_key, &user, 3600).await?;Ok(user)
}

9.3 異步任務處理

use actix_rt::spawn;// 在處理器中
pub async fn update_user(state: web::Data<AppState>,user_id: web::Path<Uuid>,form: web::Json<UpdateUser>,
) -> Result<HttpResponse, AppError> {// ... 更新邏輯// 異步發送通知let email = updated_user.email.clone();spawn(async move {if let Err(e) = send_update_notification(&email).await {tracing::error!("Failed to send notification: {}", e);}});Ok(HttpResponse::Ok().json(updated_user))
}

十、測試與部署

10.1 集成測試示例

#[cfg(test)]
mod tests {use super::*;use actix_web::{test, web, App};use sqlx::PgPool;#[actix_rt::test]async fn test_register_user() {// 初始化測試數據庫let pool = test_db_pool().await;// 創建測試應用let app = test::init_service(App::new().app_data(web::Data::new(AppState {db_pool: pool.clone(),config: test_config(),})).configure(routes::config)).await;// 創建請求let user_data = CreateUser {username: "testuser".to_string(),email: "test@example.com".to_string(),password: "password123".to_string(),};let req = test::TestRequest::post().uri("/api/auth/register").set_json(&user_data).to_request();// 發送請求let resp = test::call_service(&app, req).await;assert_eq!(resp.status(), StatusCode::CREATED);// 驗證響應let user: AuthResponse = test::read_body_json(resp).await;assert_eq!(user.user.email, user_data.email);}async fn test_db_pool() -> PgPool {// 創建測試數據庫連接池// ...}fn test_config() -> AppConfig {// 創建測試配置// ...}
}

10.2 Docker 生產部署

# 構建階段
FROM rust:1.70-slim as builderWORKDIR /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/actix-essentials /usr/local/bin
COPY migrations /app/migrations
COPY config /app/configENV APP_ENV=production
ENV RUST_LOG=infoWORKDIR /app
EXPOSE 8080
CMD ["actix-essentials"]

10.3 Kubernetes 部署配置

apiVersion: apps/v1
kind: Deployment
metadata:name: actix-api
spec:replicas: 3selector:matchLabels:app: actix-apitemplate:metadata:labels:app: actix-apispec:containers:- name: apiimage: your-registry/actix-essentials:latestenv:- name: DATABASE_URLvalueFrom:secretKeyRef:name: db-secretskey: url- name: AUTH_SECRET_KEYvalueFrom:secretKeyRef:name: auth-secretskey: secretports:- containerPort: 8080resources:limits:memory: "256Mi"cpu: "500m"livenessProbe:httpGet:path: /api/healthport: 8080initialDelaySeconds: 10periodSeconds: 10readinessProbe:httpGet:path: /api/healthport: 8080initialDelaySeconds: 5periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:name: actix-api-service
spec:selector:app: actix-apiports:- protocol: TCPport: 80targetPort: 8080

十一、性能測試結果

使用 wrk 進行壓力測試:

wrk -t12 -c400 -d30s http://localhost:8080/api/users

測試結果:

場景請求/秒平均延遲錯誤率CPU 使用內存使用
基礎實現32,50012.3ms0%78%110MB
增加緩存58,2006.7ms0%65%145MB
優化連接池45,1008.9ms0%70%125MB

總結

本文全面介紹了 Actix Web 的核心開發技術:

  1. 架構設計:深入理解 Actix Web 的請求處理流程
  2. 模塊化開發:分層架構(路由、處理器、服務、倉庫)
  3. 認證授權:JWT 認證實現與集成
  4. 錯誤處理:統一錯誤處理與響應
  5. 性能優化:連接池配置、緩存策略、異步任務
  6. 測試部署:集成測試與生產環境部署

Actix Web 憑借其卓越的性能和強大的功能,結合 Rust 的內存安全特性,成為構建高性能 Web 服務的理想選擇。其靈活的中間件系統和異步處理能力,使開發者能夠構建高并發、低延遲的現代 Web 應用。

擴展閱讀:Actix Web 官方文檔

歡迎在評論區分享你的 Actix Web 開發經驗,共同探討 Rust Web 開發的最佳實踐!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912221.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從URL到視頻:用Python和AI構建自動化內容講解視頻生成管道

摘要 本文旨在從技術層面&#xff0c;深入探討并實踐一個將任意網頁鏈接&#xff08;如飛書文檔、博客文章&#xff09;自動轉換為帶有配音和字幕的講解視頻的系統。我們將詳細拆解整個實現流程&#xff0c;覆蓋從內容抓取與解析、利用大語言模型&#xff08;LLM&#xff09;智…

Java 使用 Easy Excel 進行 Excel 數據導入導出

1. 通過 Maven 下載 Easy Excel 依賴包 在項目的 pom.xml 文件中添加以下依賴&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version> <!-- 使用最新版本 -->…

國產化條碼類庫Spire.Barcode教程:如何使用 C# 讀取 PDF 中的條碼(兩種方法輕松實現)

在 PDF 文檔的 .NET 平臺處理流程中&#xff0c;使用 C# 讀取 PDF 條碼 是一項常見需求&#xff0c;特別適用于處理掃描件或電子表單。無論是物流、金融、醫療還是制造行業&#xff0c;PDF 文檔中經常包含用于追蹤或識別的條碼。這些條碼可能是嵌入圖像&#xff0c;也可能是矢量…

2023國賽數字取證-流量分析

數據取證 - 1 A 集團的?絡安全監控系統發現惡意份?正在實施?級可持續攻擊&#xff08;APT&#xff09;&#xff0c;并抓取了部分可疑流量包。請 您根據捕捉到的流量包&#xff0c;搜尋出?絡攻擊線索&#xff0c;分解出隱藏的惡意程序&#xff0c;并分析惡意程序的?為。 …

【預約小程序】-健身房預約課程小程序——仙盟創夢IDE

東方仙盟-坐擁萬個代碼 免費報表 阿雪技術觀 讓我們積極投身于技術共享的浪潮中&#xff0c;不僅僅是作為受益者&#xff0c;更要成為貢獻者。無論是分享自己的代碼、撰寫技術博客&#xff0c;還是參與開源項目的維護和改進&#xff0c;每一個小小的舉動都可能成為推動技術進…

SmartETL中數據庫操作與流程解耦的設計與應用

正如ETL這個概念本身所指示的&#xff0c;數據庫讀寫訪問是ETL的最常用甚至是最主要的操作。現代信息系統的設計與運行基本都是圍繞數據庫展開的&#xff0c;很多應用的核心功能都是對數據庫的CRUD&#xff08;創建、檢索、更新、刪除&#xff09;操作。 SmartETL框架設計之初…

【記錄解決問題】activiti--sql 轉義符設置

一、背景 %、&#xff01;、_在sql查詢時需要轉義&#xff0c;轉義的語法 like %?2% escape ?#{escapeCharacter()}二、activiti轉義配置 String wildcardEscapeClause ""; if (this.databaseWildcardEscapeCharacter ! null && this.databaseWildcard…

Unity AR構建維護系統的以AI驅動增強現實知識檢索系統

本博客概述了為維護開發的AI驅動增強現實&#xff08;AR&#xff09;知識檢索系統的開發過程&#xff0c;該系統集成了Unity用于AR、Python服務器用于后端處理&#xff0c;以及ChatGPT用于自然語言處理。該系統允許維護工人通過AR設備&#xff08;如HoloLens 2&#xff09;查詢…

Java面向對象核心:方法值傳遞與封裝機制精講

文章目錄 Java面向對象編程核心筆記一、方法值傳遞機制1. 基本數據類型傳遞2. 引用數據類型傳遞值傳遞總結 二、面向對象核心概念1. 類與對象關系2. 類定義規范3. 對象創建與使用 三、封裝機制詳解1. 封裝三大要素2. 封裝示例&#xff08;GirlFriend類&#xff09;3. 測試類4. …

【Actix Web】構建高性能 Rust API:Actix Web 最佳實踐與進階指南

目錄 一、高性能 API 架構設計1.1 系統架構圖1.2 核心組件 二、項目初始化與配置2.1 創建項目2.2 添加依賴 (Cargo.toml)2.3 配置文件 (config/default.toml) 三、核心模塊實現3.1 應用狀態管理 (src/state.rs)3.2 數據模型定義 (src/models.rs) 四、認證與授權系統4.1 JWT 認證…

vue項目中純前端實現導出pdf文件,不需要后端處理。

在 Vue 項目中&#xff0c;純前端實現導出 PDF 文件是完全可行的。通常可以借助一些 JavaScript 庫來將 HTML 內容或 DOM 元素轉換為 PDF 并下載&#xff0c;無需后端參與。 下面介紹幾種常用的方案和實現方法&#xff1a; 推薦方案&#xff1a;使用 html2canvas jsPDF 安裝…

c++虛擬內存

常見的內存困惑 當你編寫C程序時&#xff0c;是否遇到過&#xff1a; vector申請200MB內存&#xff0c;但系統顯示只占用20MB&#xff1f;程序在低配機器上崩潰&#xff0c;報出std::bad_alloc但內存顯示充裕&#xff1f;遍歷數組時特定位置耗時突然增加&#xff1f;相同代碼…

領域驅動設計(DDD)【22】之限定建模技術

文章目錄 一 限定初識二 限定識別三 限定實現 一 限定初識 一個 員工 可以擁有多份 工作經驗&#xff0c;而各個 工作經驗 的 時間段 不能相互重疊。可以得出一個推論&#xff1a;對于一個 員工 而言&#xff0c;每個 時間段 只能有一條 工作經驗。 UML中第二種表述方式&…

《P6492 [COCI 2010/2011 #6] STEP》

題目描述 給定一個長度為 n 的字符序列 a&#xff0c;初始時序列中全部都是字符 L。 有 q 次修改&#xff0c;每次給定一個 x&#xff0c;若 ax? 為 L&#xff0c;則將 ax? 修改成 R&#xff0c;否則將 ax? 修改成 L。 對于一個只含字符 L&#xff0c;R 的字符串 s&#…

macOS,切換 space 失效,向右切換space(move right a space) 失效

背景 準確來講&#xff0c;遇到的問題是向右切換space&#xff08;move right a space) 失效&#xff0c;并向左是成功的。 在鍵盤-快捷鍵-調度中心中&#xff0c;所有的快捷鍵均可用&#xff0c;但是“向右移動一個空間”總是失效。 已經檢查過不是快捷鍵沖突的問題&#x…

網飛貓官網入口 - 免費高清影視平臺,Netflix一站觀看

網飛貓是一個專注于提供豐富影視資源的在線平臺&#xff0c;涵蓋國內外熱門電影、電視劇、動漫、綜藝等多種類型。它不僅整合了Netflix的獨家內容&#xff0c;還提供了大量高清、藍光畫質的影視作品&#xff0c;支持多語言字幕&#xff0c;滿足不同用戶的觀影需求。網飛貓的界面…

Hyper-v-中的FnOs--飛牛Nas虛擬磁盤擴容(不清除數據)

在Hyper-v下的飛牛Nas要怎么在不刪除原有虛擬磁盤數據的情況下擴容呢 OK下面開始教學&#xff08;適用于Basic模式的虛擬磁盤擴容&#xff0c;Linear沒試過&#xff09; 先關閉飛牛Nas系統 找到飛牛Nas虛擬機&#xff0c;在設置下SCSI控制器找到要擴容的虛擬磁盤&#xff0c; 點…

掌握 MySQL 的基石:全面解讀數據類型及其影響

前言 上篇文章小編講述了關于MySQL表的DDL操作&#xff0c;在那里我多次使用了MySQL的數據類型&#xff0c;但是我并沒有去講述MySQL的數據類型&#xff0c;想必各位讀者已經很好奇MySQL的數據類型都有什么了&#xff0c;今天這篇文章我將會詳細的去講述MySQL的數據類型&#x…

buildadmin 如何制作自己的插件

官方文檔指引 提示&#xff1a;若不計劃發布到應用市場&#xff0c;可省略圖片等非必要功能 參考文檔&#xff1a;https://doc.buildadmin.com/senior/module/basicInfo.html 目錄 官方文檔指引開發說明模塊開發流程模塊包結構示例安裝開發工具 總結 開發說明 目標&#xff…

【數據標注師】關鍵點標注

目錄 一、 **關鍵點標注的四大核心原則**二、 **五階能力培養體系**? **階段1&#xff1a;基礎認知筑基&#xff08;1-2周&#xff09;**? **階段2&#xff1a;復雜場景處理技能? **階段3&#xff1a;三維空間標注&#xff08;進階&#xff09;**? **階段4&#xff1a;效率…