性能优化 #

性能优化是生产环境应用的重要考量。本节将介绍Rocket应用的性能优化技巧。

异步优化 #

使用异步处理 #

rust
#[get("/users")]
async fn list_users(db: Db) -> Json<Vec<User>> {
    let users = sqlx::query_as!(User, "SELECT * FROM users")
        .fetch_all(&mut **db)
        .await
        .unwrap_or_default();
    
    Json(users)
}

并发处理 #

rust
use futures::future::join_all;

#[get("/dashboard")]
async fn dashboard(db: Db) -> Json<Dashboard> {
    let users = sqlx::query_as!(User, "SELECT * FROM users LIMIT 10");
    let posts = sqlx::query_as!(Post, "SELECT * FROM posts LIMIT 10");
    let stats = sqlx::query_as!(Stats, "SELECT * FROM stats LIMIT 1");
    
    let (users, posts, stats) = tokio::join!(
        users.fetch_all(&mut **db),
        posts.fetch_all(&mut **db),
        stats.fetch_one(&mut **db)
    );
    
    Json(Dashboard {
        users: users.unwrap_or_default(),
        posts: posts.unwrap_or_default(),
        stats: stats.ok(),
    })
}

连接池优化 #

配置连接池 #

toml
[default.databases.my_db]
url = "postgres://user:password@localhost/mydb"
pool_size = 20
timeout = 5

连接池大小计算 #

text
pool_size = CPU核心数 * 2 + 有效磁盘数

缓存策略 #

内存缓存 #

rust
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use std::time::{Duration, Instant};

pub struct Cache {
    data: Arc<RwLock<HashMap<String, CacheEntry>>>,
}

struct CacheEntry {
    value: String,
    expires_at: Instant,
}

impl Cache {
    pub fn new() -> Self {
        Self {
            data: Arc::new(RwLock::new(HashMap::new())),
        }
    }
    
    pub async fn get(&self, key: &str) -> Option<String> {
        let data = self.data.read().await;
        data.get(key)
            .filter(|e| e.expires_at > Instant::now())
            .map(|e| e.value.clone())
    }
    
    pub async fn set(&self, key: String, value: String, ttl: Duration) {
        let mut data = self.data.write().await;
        data.insert(key, CacheEntry {
            value,
            expires_at: Instant::now() + ttl,
        });
    }
}

使用缓存 #

rust
use std::time::Duration;

#[get("/users/<id>")]
async fn get_user_cached(
    id: u32,
    db: Db,
    cache: &State<Cache>,
) -> Option<Json<User>> {
    let cache_key = format!("user:{}", id);
    
    if let Some(cached) = cache.get(&cache_key).await {
        return Some(Json(serde_json::from_str(&cached).unwrap()));
    }
    
    let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
        .fetch_optional(&mut **db)
        .await
        .ok()??;
    
    cache.set(
        cache_key,
        serde_json::to_string(&user).unwrap(),
        Duration::from_secs(300),
    ).await;
    
    Some(Json(user))
}

响应压缩 #

启用压缩 #

rust
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Request, Response};

struct Compression;

#[rocket::async_trait]
impl Fairing for Compression {
    fn info(&self) -> Info {
        Info {
            name: "Response Compression",
            kind: Kind::Response,
        }
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        if let Some(accept) = request.headers().get_one("Accept-Encoding") {
            if accept.contains("gzip") {
                response.set_header(rocket::http::Header::new("Content-Encoding", "gzip"));
            }
        }
    }
}

查询优化 #

分页查询 #

rust
#[get("/users?<page>&<per_page>")]
async fn list_users_paginated(
    db: Db,
    page: Option<i64>,
    per_page: Option<i64>,
) -> Json<Vec<User>> {
    let page = page.unwrap_or(1);
    let per_page = per_page.unwrap_or(20).min(100);
    let offset = (page - 1) * per_page;
    
    let users = sqlx::query_as!(
        User,
        "SELECT * FROM users ORDER BY id LIMIT $1 OFFSET $2",
        per_page,
        offset
    )
    .fetch_all(&mut **db)
    .await
    .unwrap_or_default();
    
    Json(users)
}

索引优化 #

sql
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);

静态资源优化 #

缓存静态资源 #

rust
use rocket::fs::NamedFile;
use rocket::http::Header;

#[get("/static/<file..>")]
async fn static_files(file: std::path::PathBuf) -> Option<(NamedFile, Header<'static>)> {
    let path = std::path::Path::new("static/").join(file);
    let named_file = NamedFile::open(&path).await.ok()?;
    
    let cache_header = Header::new("Cache-Control", "public, max-age=31536000");
    Some((named_file, cache_header))
}

性能监控 #

请求计时 #

rust
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Request, Data, Response};
use std::time::Instant;

struct RequestTimer;

#[rocket::async_trait]
impl Fairing for RequestTimer {
    fn info(&self) -> Info {
        Info {
            name: "Request Timer",
            kind: Kind::Request | Kind::Response,
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) {
        request.local_cache(|| Instant::now());
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        if let Some(start) = request.local_cache::<Instant>().get() {
            let duration = start.elapsed();
            response.set_header(rocket::http::Header::new(
                "X-Response-Time",
                format!("{:.2}ms", duration.as_secs_f64() * 1000.0)
            ));
        }
    }
}

性能检查清单 #

数据库 #

  • [ ] 使用连接池
  • [ ] 添加适当索引
  • [ ] 使用分页查询
  • [ ] 避免 N+1 查询

缓存 #

  • [ ] 实现热点数据缓存
  • [ ] 设置合理的过期时间
  • [ ] 使用缓存预热

网络 #

  • [ ] 启用响应压缩
  • [ ] 设置静态资源缓存
  • [ ] 减少请求大小

代码 #

  • [ ] 使用异步处理
  • [ ] 避免阻塞操作
  • [ ] 优化热点代码

完整示例 #

rust
#[macro_use] extern crate rocket;

use rocket::serde::json::Json;
use rocket_db_pools::{Database, Connection, sqlx};
use sqlx::PgPool;
use std::time::{Duration, Instant};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

#[derive(Database)]
#[database("postgres")]
struct Db(PgPool);

struct Cache {
    data: Arc<RwLock<HashMap<String, (String, Instant)>>>,
}

impl Cache {
    fn new() -> Self {
        Self {
            data: Arc::new(RwLock::new(HashMap::new())),
        }
    }
    
    async fn get(&self, key: &str) -> Option<String> {
        let data = self.data.read().await;
        data.get(key)
            .filter(|(_, exp)| exp > &Instant::now())
            .map(|(v, _)| v.clone())
    }
    
    async fn set(&self, key: String, value: String, ttl: Duration) {
        let mut data = self.data.write().await;
        data.insert(key, (value, Instant::now() + ttl));
    }
}

#[derive(sqlx::FromRow, rocket::serde::Serialize)]
struct User {
    id: i32,
    name: String,
}

#[get("/users/<id>")]
async fn get_user(id: i32, mut db: Connection<Db>, cache: &rocket::State<Cache>) -> Option<Json<User>> {
    let cache_key = format!("user:{}", id);
    
    if let Some(cached) = cache.get(&cache_key).await {
        return Some(Json(serde_json::from_str(&cached).unwrap()));
    }
    
    let user = sqlx::query_as!(User, "SELECT id, name FROM users WHERE id = $1", id)
        .fetch_optional(&mut **db)
        .await
        .ok()??;
    
    cache.set(cache_key, serde_json::to_string(&user).unwrap(), Duration::from_secs(60)).await;
    
    Some(Json(user))
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(Db::init())
        .manage(Cache::new())
        .mount("/api", routes![get_user])
}

下一步 #

掌握了性能优化后,让我们继续学习 RESTful API,开始实战项目。

最后更新:2026-03-28