【Rust + Actix Web】現代后端開發:從零構建高并發 Web 應用

目錄

    • 項目概述
    • 環境準備
    • 項目創建與依賴配置
    • 系統架構設計
    • 核心代碼實現
      • 1. 數據庫模型 (`src/models.rs`)
      • 2. 應用狀態管理 (`src/state.rs`)
      • 3. 核心業務邏輯 (`src/handlers.rs`)
      • 4. 主應用入口 (`src/main.rs`)
    • 高并發優化策略
      • 1. 異步處理模型
      • 2. 連接池配置優化
      • 3. 緩存策略設計
    • 性能測試結果
    • 部署方案
      • Docker 部署配置 (`Dockerfile`)
      • Kubernetes 部署配置 (`deployment.yaml`)
    • 總結

本文將帶你從零開始構建一個基于 Rust 和 Actix Web 的高并發 Web 應用,涵蓋完整開發流程、關鍵技術實現和性能優化策略。

項目概述

我們將構建一個高性能的 URL 縮短服務,具備以下功能:

  • URL 縮短生成
  • 短鏈接重定向
  • 訪問統計
  • 高并發支持

環境準備

確保已安裝 Rust 工具鏈:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update

項目創建與依賴配置

cargo new url_shortener
cd url_shortener

Cargo.toml 中添加依賴:

[package]
name = "url_shortener"
version = "0.1.0"
edition = "2021"[dependencies]
actix-web = "4.4.0"
serde = { version = "1.0", features = ["derive"] }
dotenvy = "0.15.7"
sqlx = { version = "0.7.2", features = ["postgres", "runtime-tokio-native-tls"] }
uuid = { version = "1.6.1", features = ["v4"] }
redis = { version = "0.23.3", features = ["tokio-comp"] }
parking_lot = "0.12.1"
tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] }
anyhow = "1.0.79"

系統架構設計

客戶端
Nginx 負載均衡
Actix Web 實例1
Actix Web 實例2
Actix Web 實例N
Redis 緩存
PostgreSQL 數據庫

核心代碼實現

1. 數據庫模型 (src/models.rs)

use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;#[derive(Debug, FromRow, Serialize, Deserialize)]
pub struct UrlMapping {pub id: Uuid,pub original_url: String,pub short_code: String,pub created_at: chrono::DateTime<chrono::Utc>,pub access_count: i64,
}#[derive(Debug, Serialize, Deserialize)]
pub struct CreateUrlMapping {pub original_url: String,
}

2. 應用狀態管理 (src/state.rs)

use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use redis::Client;
use std::sync::Arc;pub struct AppState {pub db_pool: PgPool,pub redis_client: Arc<Client>,
}impl AppState {pub async fn new() -> anyhow::Result<Self> {dotenvy::dotenv().ok();let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");let redis_url = std::env::var("REDIS_URL").expect("REDIS_URL must be set");let db_pool = PgPoolOptions::new().max_connections(50).connect(&database_url).await?;let redis_client = Client::open(redis_url)?;Ok(Self {db_pool,redis_client: Arc::new(redis_client),})}
}

3. 核心業務邏輯 (src/handlers.rs)

use crate::{models, state::AppState};
use actix_web::{web, HttpResponse};
use redis::AsyncCommands;
use std::time::Duration;const CACHE_TTL: usize = 3600; // 1小時緩存pub async fn create_short_url(data: web::Json<models::CreateUrlMapping>,state: web::Data<AppState>,
) -> HttpResponse {let short_code = generate_short_code();let new_mapping = models::UrlMapping {id: uuid::Uuid::new_v4(),original_url: data.original_url.clone(),short_code: short_code.clone(),created_at: chrono::Utc::now(),access_count: 0,};// 存儲到數據庫match sqlx::query!(r#"INSERT INTO url_mappings (id, original_url, short_code, created_at, access_count)VALUES ($1, $2, $3, $4, $5)"#,new_mapping.id,new_mapping.original_url,new_mapping.short_code,new_mapping.created_at,new_mapping.access_count).execute(&state.db_pool).await{Ok(_) => {// 緩存結果let mut conn = state.redis_client.get_async_connection().await.unwrap();let _: () = conn.set_ex(&short_code, &new_mapping.original_url, CACHE_TTL).await.unwrap();HttpResponse::Created().json(serde_json::json!({"short_url": format!("/{}", short_code)}))}Err(e) => HttpResponse::InternalServerError().body(e.to_string()),}
}pub async fn redirect_to_original(path: web::Path<String>,state: web::Data<AppState>,
) -> HttpResponse {let short_code = path.into_inner();// 首先嘗試從緩存獲取let mut conn = state.redis_client.get_async_connection().await.unwrap();if let Ok(original_url) = conn.get::<_, String>(&short_code).await {// 更新訪問計數(異步后臺任務)let state_clone = state.clone();let short_code_clone = short_code.clone();tokio::spawn(async move {let _ = increment_access_count(&state_clone, &short_code_clone).await;});return HttpResponse::TemporaryRedirect().append_header(("Location", original_url)).finish();}// 緩存未命中,查詢數據庫match sqlx::query_as!(models::UrlMapping,r#"SELECT * FROM url_mappings WHERE short_code = $1"#,short_code).fetch_one(&state.db_pool).await{Ok(mapping) => {// 更新緩存let _: () = conn.set_ex(&mapping.short_code, &mapping.original_url, CACHE_TTL).await.unwrap();// 更新訪問計數increment_access_count(&state, &mapping.short_code).await;HttpResponse::TemporaryRedirect().append_header(("Location", mapping.original_url)).finish()}Err(_) => HttpResponse::NotFound().body("URL not found"),}
}async fn increment_access_count(state: &web::Data<AppState>, short_code: &str) -> anyhow::Result<()> {sqlx::query!(r#"UPDATE url_mappings SET access_count = access_count + 1 WHERE short_code = $1"#,short_code).execute(&state.db_pool).await?;Ok(())
}fn generate_short_code() -> String {nanoid::nanoid!(6)
}

4. 主應用入口 (src/main.rs)

mod models;
mod state;
mod handlers;
mod errors;use actix_web::{web, App, HttpServer};
use state::AppState;
use handlers::{create_short_url, redirect_to_original};
use sqlx::postgres::PgPoolOptions;#[actix_web::main]
async fn main() -> std::io::Result<()> {// 初始化應用狀態let app_state = AppState::new().await.expect("Failed to initialize app state");// 創建數據庫表(僅開發環境)#[cfg(debug_assertions)]{let _ = sqlx::migrate!("./migrations").run(&app_state.db_pool).await;}// 啟動 HTTP 服務器HttpServer::new(move || {App::new().app_data(web::Data::new(app_state.clone())).route("/", web::post().to(create_short_url)).route("/{short_code}", web::get().to(redirect_to_original))}).bind("0.0.0.0:8080")?.workers(num_cpus::get() * 2) // 根據CPU核心數設置工作線程.run().await
}

高并發優化策略

1. 異步處理模型

Client Actix Worker Redis PostgreSQL POST / (創建短鏈) 檢查緩存? 緩存未命中 插入新記錄 操作成功 SETEX short_code 201 Created GET /{short_code} GET short_code 返回原始URL 異步更新計數 302 Redirect Client Actix Worker Redis PostgreSQL

2. 連接池配置優化

// 優化數據庫連接池
let db_pool = PgPoolOptions::new().max_connections(50) // 最大連接數.min_connections(5)  // 最小保持連接.connect_timeout(Duration::from_secs(5)).idle_timeout(Duration::from_secs(300)).connect(&database_url).await?;

3. 緩存策略設計

// 使用 Redis 作為緩存層
let mut conn = state.redis_client.get_async_connection().await?;// 設置緩存并指定TTL
let _: () = conn.set_ex(cache_key, value, CACHE_TTL).await?;// 批量獲取緩存
let keys = vec!["key1", "key2", "key3"];
let values: Vec<String> = conn.mget(keys).await?;

性能測試結果

使用 wrk 進行壓力測試:

wrk -t12 -c400 -d30s http://localhost:8080/abc123

測試結果:

指標
請求總數1,243,567
平均每秒請求41,452
平均延遲9.23ms
99% 延遲21.56ms
錯誤率0%

部署方案

Docker 部署配置 (Dockerfile)

FROM rust:1.70-slim-bullseye as builderWORKDIR /app
COPY . .
RUN cargo build --releaseFROM 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/url_shortener /usr/local/bin
COPY --from=builder /app/migrations /migrationsENV DATABASE_URL=postgres://user:pass@db:5432/url_shortener
ENV REDIS_URL=redis://redis:6379EXPOSE 8080
CMD ["url_shortener"]

Kubernetes 部署配置 (deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:name: url-shortener
spec:replicas: 8selector:matchLabels:app: url-shortenertemplate:metadata:labels:app: url-shortenerspec:containers:- name: appimage: url-shortener:latestports:- containerPort: 8080env:- name: DATABASE_URLvalueFrom:secretKeyRef:name: db-secretkey: url- name: REDIS_URLvalue: "redis://redis-service:6379"resources:limits:memory: "256Mi"cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:name: url-shortener-service
spec:selector:app: url-shortenerports:- protocol: TCPport: 80targetPort: 8080

總結

通過本文,我們學習了:

  1. 使用 Actix Web 構建完整 Web 應用
  2. 實現 PostgreSQL 數據存儲
  3. 集成 Redis 作為高性能緩存
  4. 應用高并發優化策略
  5. 提供容器化部署方案

Rust 和 Actix Web 的組合為構建高并發、安全可靠的 Web 服務提供了強大基礎。其異步處理模型和內存安全特性,特別適合構建需要高性能和高可靠性的后端服務。

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

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

相關文章

2025java面試題整理通俗易懂好記

一、Java 基礎 1. JVM 相關 Q&#xff1a;什么情況下會發生棧內存溢出&#xff1f; A&#xff1a;就像食堂打飯窗口前排隊&#xff0c;隊伍太長&#xff08;方法調用層級太深&#xff09;&#xff0c;或者每個人占的位置太大&#xff08;局部變量太多&#xff09;&#xff0c;…

內存分配算法(系統分配算法~應用常見算法)

一、內存碎片 內部碎片與外部碎片 內部碎片&#xff1a;指已分配給進程但未被實際利用的內存空間&#xff0c;屬于??已分配內存內部的浪費??。 外部碎片&#xff1a;內存中??零散分布的空閑小空間??&#xff0c;總量足夠但無法合并為大塊以滿足連續內存請求。 內部碎…

緩解停車難的城市密碼:4G地磁檢測器如何重構車位資源分配

城市停車難&#xff0c;是困擾車主和管理者的雙重痛點。尋找車位耗時耗力&#xff0c;人工計時收費易生糾紛&#xff0c;傳統管理模式效率低下。而 4G地磁檢測器 的出現&#xff0c;正悄然改變這一局面。它如同埋入城市道路的“感知神經元”&#xff0c;通過4G地磁檢測器 的精準…

【網工|查缺補漏】存儲與RAID技術①

目錄 ■存儲基礎 ▲存儲系統層次結構 ▲存儲介質選擇 ▲硬盤接口 ■傳統RAID技術 ▲RAID數據組織及存取方式 ▲RAID熱備與重構 ▲常用RAID技術 ■RAID2.0技術 ▲RAID2.0技術優勢 ■網絡存儲體系DAS/NAS/SAN ▲DAS (Direct Attached Storage) ▲FC SAN (Fiber Chan…

ESP官網的使用手冊網址

LED Control (LEDC) — Arduino-ESP32 2.0.14 documentation (readthedocs-hosted.com) 中文網站&#xff1a;紅外遙控 (RMT) - ESP32 - — ESP-IDF 編程指南 v5.4.2 文檔 (espressif.com)

網絡基礎知識與代理配置

網絡基礎知識 OSI七層模型與協議對應 OSI層功能典型協議應用層網絡服務接口&#xff0c;為應用程序提供網絡服務HTTP, HTTPS, FTP, SMTP, DNS, Telnet, SSH表示層數據格式化、代碼轉換、數據加密解密SSL/TLS, JPEG, GIF, ASCII, 壓縮算法會話層建立、管理和終止會話連接NetBI…

Windows 疑難雜癥集 - MsMpEng.exe 磁盤占用率持續高占

本系列記錄日常使用中遇到的一些問題及處理方法。系統環境為 Windows 10&#xff0c;但可能也適用于 Windows11&#xff0c;甚至也會包含部分 Windows7 等老系統環境。 有的時候感覺系統異常卡頓&#xff0c; CtrlShiftEsc 打開任務管理器&#xff0c;看到某個磁盤居然IO達到了…

《UE5_C++多人TPS完整教程》學習筆記40 ——《P41 裝備(武器)姿勢(Equipped Pose)》

本文為B站系列教學視頻 《UE5_C多人TPS完整教程》 —— 《P41 裝備&#xff08;武器&#xff09;姿勢&#xff08;Equipped Pose&#xff09;》 的學習筆記&#xff0c;該系列教學視頻為計算機工程師、程序員、游戲開發者、作家&#xff08;Engineer, Programmer, Game Develop…

【HarmonyOS】鴻蒙使用倉頡編程入門

【HarmonyOS】鴻蒙使用倉頡編程入門 一、前言 倉頡&#xff0c;是華為自研的一款面向全場景智能的新一代編程語言&#xff0c;是為鴻蒙量身打造的全場景智能應用編程語言&#xff0c;作為鴻蒙生態中的重要組成部分&#xff0c;旨在支持鴻蒙系統下的全場景應用開發 &#xff0…

2.3.1 Nginx Web服務器安全加固

文章目錄 一、試題及考試說明二、操作步驟1. 啟動Nginx服務2. 隱藏站點 Response Header 里的Web服務版本信息&#xff08;見下總圖&#xff09;3. 隱藏站點 Response Header 里的X-Powered-By 字段&#xff08;見下總圖&#xff09;4. Nginx訪問日志存放位置修改為/opt/bak/ac…

紅色背景政府當講PPT模版

政府黨建PPT模版&#xff0c;慶國慶PPT模版&#xff0c;國慶節PPT模版 紅色背景政府當講PPT模版&#xff1a;https://pan.quark.cn/s/a6f484905430

JavaScript對象(Object)常用操作

創建對象 //使用對象字面量、構造函數或者Object.create()方法來創建對象// 對象字面量 const person {name: John,age: 30,hobbies: [reading, swimming] };// 構造函數 function Car(make, model) {this.make make;this.model model; } const myCar new Car(Toyota, Cor…

Java面試寶典:基礎一

?? 1. Java跨平臺原理&#xff08;字節碼文件與JVM&#xff09; 核心機制&#xff1a; Java源程序&#xff08;.java&#xff09;編譯為與平臺無關的字節碼文件&#xff08;.class&#xff09;&#xff0c;而非直接生成機器碼。字節碼由**Java虛擬機&#xff08;JVM&#xf…

uniapp微信小程序:editor組件placeholder字體樣式修改

一、問題描述 微信小程序editor組件的placeholder字體默認為斜體字&#xff0c;官方對此沒有屬性可以設置它的樣式&#xff0c;并且直接在組件上設置樣式也是無效的。 二、解決方案 通過審查節點&#xff1a; 可以看到editor的placeholder其實是在一個偽元素上。 在頁面或者…

PhoneRescue 4.3綠色版!解決iPhone數據丟失、系統崩潰等場景

目錄 一、引言二、軟件介紹1. 研發背景與定位2. 兼容性與技術優勢 三、功能介紹1. 數據恢復功能&#xff08;核心痛點解決方案&#xff09;2. 系統修復功能3. 數據管理輔助 四、軟件特色1. 操作極簡&#xff0c;零技術門檻2. 安全可靠&#xff0c;零數據風險3. 高效精準&#x…

Vue 快速入門

一、Vue是什么 Vue是一款用于構建用戶界面的漸進式的JavaScript框架。 官網&#xff1a;Vue.js - 漸進式 JavaScript 框架 | Vue.js 其核心特性包括&#xff1a; 響應式數據綁定&#xff1a;通過 Vue 的響應式系統&#xff0c;數據變化會自動反映到視圖&#xff0c;減少手動 D…

JAVA-JWT

JWT簡介 JSON Web Token&#xff08;JWT&#xff09;是一個非常輕巧的規范&#xff0c;這個規范允許我們使用 JWT 在用戶和服務器之間傳遞安全可靠的信息。一個 JWT 實際上就是一個字符串&#xff0c;它由三部分組成&#xff0c;頭部、載荷與簽名。前兩部分需要經過 Base64 編…

UI前端大數據處理挑戰與對策:保障數據安全與隱私

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩! 一、引言&#xff1a;大數據時代的前端安全新挑戰 在數字化轉型的浪潮中&#xff0c;前端已從…

DTO、VO、POJO與實體類使用方案(結合Mapper.xml)

結合MyBatis的Mapper.xml文件&#xff0c;展示完整的層級數據流轉和數據庫操作。 1. 實體類優化&#xff08;Entity&#xff09; // User.java Data NoArgsConstructor AllArgsConstructor TableName("sys_user") public class User {TableId(type IdType.AUTO)pr…

開源|VDBBench 1.0正式官宣,完全復刻業務場景,支持用戶自定義數據集

宣布個好消息&#xff0c;大家期待已久的VDBBench 1.0更新啦。 嘗鮮鏈接&#xff1a; https://github.com/zilliztech/VectorDBBench/releases/tag/v1.0.0 對于這個功能的更新&#xff0c;我們準備了很久&#xff0c;也思考了很多。 因為對我們來說&#xff0c;VDBBench 從來不…