目录

    • 项目概述
    • 环境准备
    • 项目创建与依赖配置
    • 系统架构设计
    • 核心代码实现
      • 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 从来不…