是时候体验真正的 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 开发者了吗?