第 14 章:Spring Boot 用腻了吗?启动时间长,内存占用高,JVM 调优让人头疼?

是时候体验真正的 Web 服务性能了。

Rust 的 Web 框架不需要"魔法",不需要反射,不需要依赖注入的复杂性。它们只需要一件事:让你的 API 快到飞起。

准备好被 Rust Web 服务的性能震撼吧。

Spring Boot:舒适的性能杀手

Spring Boot 的"便利"代价

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserService userService;  // 运行时注入,编译期不知道

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        // 每个请求都经过:
        // 1. Servlet 容器
        // 2. Spring MVC 分发器
        // 3. 控制器映射
        // 4. 方法参数解析
        // 5. 反序列化/序列化
        // 6. 异常处理链
        return ResponseEntity.ok(userService.findById(id));
    }
}

看起来简洁?代价是什么?

  • 启动时间:Spring Boot 应用冷启动需要 10-30 秒
  • 内存占用:基础应用就要 200-500MB 内存
  • 运行时开销:反射、代理、依赖查找的性能损失
  • JVM 调优:垃圾回收、堆内存设置的复杂性

Rust Web 框架:性能至上的设计

Axum:现代化的选择

use axum::{
    extract::Path,
    http::StatusCode,
    response::Json,
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};
use tokio;

#[derive(Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

// 处理函数:编译期确定,零运行时开销
async fn get_user(Path(id): Path<u64>) -> Result<Json<User>, StatusCode> {
    // 业务逻辑
    let user = User {
        id,
        name: "Alice".to_string(),
        email: "[email protected]".to_string(),
    };

    Ok(Json(user))
}

async fn create_user(Json(user): Json<User>) -> Result<Json<User>, StatusCode> {
    // 创建用户逻辑
    println!("Creating user: {:?}", user);
    Ok(Json(user))
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/users/:id", get(get_user))
        .route("/users", post(create_user));

    // 启动服务器:毫秒级启动
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();

    println!("Server running on http://0.0.0.0:3000");
    axum::serve(listener, app).await.unwrap();
}

编译完成,立即启动,内存占用不到 10MB。

Actix-web:性能的标杆

use actix_web::{web, App, HttpResponse, HttpServer, Result};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

async fn get_user(path: web::Path<u32>) -> Result<HttpResponse> {
    let user_id = path.into_inner();
    let user = User {
        id: user_id,
        name: "Bob".to_string(),
        email: "[email protected]".to_string(),
    };

    Ok(HttpResponse::Ok().json(user))
}

async fn create_user(user: web::Json<User>) -> Result<HttpResponse> {
    println!("Creating user: {:?}", user);
    Ok(HttpResponse::Created().json(&*user))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/users/{id}", web::get().to(get_user))
            .route("/users", web::post().to(create_user))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Actix-web 在 TechEmpower 基准测试中经常排名第一。

性能对比:数据说话

启动时间

Spring Boot:

$ time java -jar myapp.jar
# 冷启动:15-30 秒
# 热启动:5-10 秒

Rust (Axum/Actix):

$ time ./target/release/myapp
# 启动时间:50-200 毫秒

差距:100-300 倍的启动速度优势。

内存占用

Spring Boot:

  • 最小应用:200-300MB
  • 生产应用:500MB-2GB
  • JVM 堆内存:需要预留额外空间

Rust Web 服务:

  • 最小应用:5-15MB
  • 复杂应用:50-200MB
  • 无垃圾收集:内存使用可预测

差距:10-50 倍的内存效率优势。

吞吐量

基准测试结果(TechEmpower):

框架 每秒请求数 延迟 (p99)
Spring Boot 50,000 100ms
Axum 800,000 1ms
Actix-web 1,200,000 0.5ms

性能差距:20-30 倍的吞吐量优势。

完整的 REST API 示例

项目设置

# Cargo.toml
[package]
name = "rust-api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
uuid = { version = "1.0", features = ["v4", "serde"] }

数据模型

use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct User {
    pub id: Uuid,
    pub name: String,
    pub email: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
    pub name: String,
    pub email: String,
}

#[derive(Debug, Deserialize)]
pub struct UpdateUserRequest {
    pub name: Option<String>,
    pub email: Option<String>,
}

完整的 CRUD API

use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::Json,
    routing::{delete, get, post, put},
    Router,
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use uuid::Uuid;

// 简单的内存存储(实际项目中用数据库)
type UserStore = Arc<Mutex<HashMap<Uuid, User>>>;

async fn create_user(
    State(store): State<UserStore>,
    Json(req): Json<CreateUserRequest>,
) -> Result<Json<User>, StatusCode> {
    let user = User {
        id: Uuid::new_v4(),
        name: req.name,
        email: req.email,
        created_at: chrono::Utc::now(),
    };

    store.lock().unwrap().insert(user.id, user.clone());
    Ok(Json(user))
}

async fn get_user(
    Path(id): Path<Uuid>,
    State(store): State<UserStore>,
) -> Result<Json<User>, StatusCode> {
    let store = store.lock().unwrap();
    match store.get(&id) {
        Some(user) => Ok(Json(user.clone())),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn update_user(
    Path(id): Path<Uuid>,
    State(store): State<UserStore>,
    Json(req): Json<UpdateUserRequest>,
) -> Result<Json<User>, StatusCode> {
    let mut store = store.lock().unwrap();
    match store.get_mut(&id) {
        Some(user) => {
            if let Some(name) = req.name {
                user.name = name;
            }
            if let Some(email) = req.email {
                user.email = email;
            }
            Ok(Json(user.clone()))
        }
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn delete_user(
    Path(id): Path<Uuid>,
    State(store): State<UserStore>,
) -> Result<StatusCode, StatusCode> {
    let mut store = store.lock().unwrap();
    match store.remove(&id) {
        Some(_) => Ok(StatusCode::NO_CONTENT),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn list_users(
    State(store): State<UserStore>,
) -> Result<Json<Vec<User>>, StatusCode> {
    let store = store.lock().unwrap();
    let users: Vec<User> = store.values().cloned().collect();
    Ok(Json(users))
}

#[tokio::main]
async fn main() {
    let store: UserStore = Arc::new(Mutex::new(HashMap::new()));

    let app = Router::new()
        .route("/users", post(create_user))
        .route("/users", get(list_users))
        .route("/users/:id", get(get_user))
        .route("/users/:id", put(update_user))
        .route("/users/:id", delete(delete_user))
        .with_state(store);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();

    println!("Server running on http://0.0.0.0:3000");
    axum::serve(listener, app).await.unwrap();
}

完整的 CRUD API,编译后立即可用,性能爆表。

中间件和错误处理

全局错误处理

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use serde_json::json;

#[derive(Debug)]
enum AppError {
    NotFound,
    InternalServerError,
    BadRequest(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "Resource not found"),
            AppError::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"),
            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.as_str()),
        };

        let body = Json(json!({
            "error": error_message,
        }));

        (status, body).into_response()
    }
}

日志中间件

use axum::{
    middleware::{self, Next},
    response::Response,
    http::Request,
};
use std::time::Instant;

async fn logging_middleware<B>(
    request: Request<B>,
    next: Next<B>,
) -> Response {
    let method = request.method().clone();
    let uri = request.uri().clone();
    let start = Instant::now();

    let response = next.run(request).await;

    let elapsed = start.elapsed();
    println!("{} {} - {}ms", method, uri, elapsed.as_millis());

    response
}

// 在路由中使用
let app = Router::new()
    .route("/users", get(list_users))
    .layer(middleware::from_fn(logging_middleware));

部署:Docker 化的极致效率

Dockerfile

# 多阶段构建
FROM rust:1.75 as builder

WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim

# 只复制二进制文件
COPY --from=builder /app/target/release/rust-api /usr/local/bin/rust-api

EXPOSE 3000
CMD ["rust-api"]

镜像大小比较:

  • Spring Boot 镜像:200-500MB
  • Rust 应用镜像:20-50MB

启动时间比较:

  • Spring Boot 容器:10-30 秒
  • Rust 应用容器:100-500 毫秒

写在最后:Web 开发的范式转变

Spring Boot 的哲学:用便利换性能。

Rust Web 的哲学:用明确性换极致性能。

当你从 Spring 转向 Rust Web 开发,你会体验到:

  • 启动速度:从分钟级到毫秒级
  • 内存占用:从几百 MB 到几十 MB
  • 吞吐量:从万级到百万级
  • 资源效率:一台服务器顶十台
  • 部署简单:单个二进制文件,无需 JVM

代价是什么?

  • 需要更明确地处理错误
  • 需要理解所有权和生命周期
  • 没有运行时"魔法",一切都要明确声明

但这些"代价"带来的是:

  • 编译期安全保证
  • 可预测的性能表现
  • 极致的资源效率
  • 无需复杂的调优

这就是现代 Web 服务应该有的样子。

下一章是我们的最后一章,我们要总结整个 Rust 学习之旅。准备好从 Java 程序员完全蜕变为 Rust 开发者了吗?