第十章:测试、优化与生产部署
博客v1.0系列教程(Rust)博客 v1.0 系列教程 (Rust)
10.1 集成测试
使用 SQLx 的测试数据库进行集成测试:
// tests/api_test.rs
use sqlx::PgPool;
#[sqlx::test]
async fn test_create_article(pool: PgPool) {
let service = ArticleService::new(pool);
let dto = CreateArticleDto {
title: "Test Article".into(),
content: "# Hello\nWorld".into(),
category: Some("Test".into()),
tags: Some(vec!["rust".into(), "test".into()]),
};
let article = service.create_article(dto).await.unwrap();
assert_eq!(article.title, Some("Test Article".into()));
assert_eq!(article.category, Some("Test".into()));
assert!(article.id > 0);
}
#[sqlx::test]
async fn test_article_not_found(pool: PgPool) {
let service = ArticleService::new(pool);
let result = service.get_article(99999).await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
AppError::NotFound(_)
));
}
10.2 API 测试
use axum::http::StatusCode;
#[sqlx::test]
async fn test_article_list_api(pool: PgPool) {
let app = create_test_app(pool).await;
let response = app
.get("/api/article")
.await;
assert_eq!(response.status_code(), StatusCode::OK);
let body: PageResult<Article> = response.json();
assert!(body.items.len() >= 0);
}
async fn create_test_app(pool: PgPool) -> Router {
Router::new()
.route("/api/article", get(list_articles))
.with_state(pool)
}
10.3 性能优化
连接池优化
pub async fn init_pool(database_url: &str) -> Result<PgPool, sqlx::Error> {
PgPoolOptions::new()
.max_connections(50)
.min_connections(10)
.max_lifetime(Duration::from_secs(1800))
.idle_timeout(Duration::from_secs(600))
.acquire_timeout(Duration::from_secs(3))
.connect(database_url)
.await
}
查询优化
// 使用索引覆盖查询
#[derive(Debug, FromRow)]
pub struct ArticleSummary {
pub id: i64,
pub title: Option<String>,
pub category: Option<String>,
pub create_time: Option<NaiveDateTime>,
}
// 只查询需要的字段
let summaries = sqlx::query_as::<_, ArticleSummary>(
"SELECT id, title, category, create_time FROM article_models
ORDER BY create_time DESC LIMIT $1"
)
.bind(20)
.fetch_all(&pool)
.await?;
响应缓存
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct Cache<T> {
data: Arc<RwLock<HashMap<String, (T, Instant)>>>,
ttl: Duration,
}
impl<T: Clone + Send + Sync + 'static> Cache<T> {
pub fn new(ttl: Duration) -> Self {
Cache {
data: Arc::new(RwLock::new(HashMap::new())),
ttl,
}
}
pub async fn get(&self, key: &str) -> Option<T> {
let map = self.data.read().await;
if let Some((value, expires)) = map.get(key) {
if expires.elapsed() < self.ttl {
return Some(value.clone());
}
}
None
}
pub async fn set(&self, key: String, value: T) {
let mut map = self.data.write().await;
map.insert(key, (value, Instant::now()));
}
}
10.4 Release 构建优化
# Cargo.toml
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
strip = true
# 构建 release 版
cargo build --release
# 查看二进制大小
ls -lh target/release/rabitlogic-blog
10.5 Docker 部署
# Dockerfile
FROM rust:1.81-slim-bookworm AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/rabitlogic-blog .
COPY --from=builder /app/templates ./templates
COPY --from=builder /app/articles ./articles
COPY --from=builder /app/migrations ./migrations
COPY --from=builder /app/assets ./assets
COPY --from=builder /app/.env.example .env
EXPOSE 5051
CMD ["./rabitlogic-blog"]
Docker Compose
version: '3.8'
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: blog_ssr
POSTGRES_USER: blog
POSTGRES_PASSWORD: blog123
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blog"]
interval: 5s
timeout: 5s
retries: 5
app:
build: .
ports:
- "5051:5051"
environment:
DATABASE_URL: postgres://blog:blog123@postgres:5432/blog_ssr
SERVER_HOST: 0.0.0.0
SERVER_PORT: 5051
JWT_SECRET: change-this-in-production
RUST_LOG: info
depends_on:
postgres:
condition: service_healthy
volumes:
pgdata:
10.6 .env 示例
DATABASE_URL=postgres://blog:blog123@localhost:5432/blog_ssr
SERVER_HOST=0.0.0.0
SERVER_PORT=5051
JWT_SECRET=your-production-secret-key-change-me
RUST_LOG=info
10.7 一键部署脚本
# deploy.ps1
Write-Host "Building Rust backend..." -ForegroundColor Green
cargo build --release
Write-Host "Deploying with Docker..." -ForegroundColor Green
docker-compose up -d --build
Write-Host "Done! Server running on http://localhost:5051" -ForegroundColor Green
至此,博客 v1.0 (Rust 后端) 系列教程完成!我们深入学习了 Rust 异步编程、Axum 框架、SQLx 数据库操作、JWT 认证、RESTful API 设计、错误处理、文件上传、中间件、日志系统以及生产部署的完整知识体系。
rusttestingoptimizationdeploydocker