在当今高并发的网络环境下,异步编程已成为构建高性能服务的标配。Rust 语言凭借其出色的性能表现和内存安全性,结合强大的异步生态,正在成为构建下一代 Web 服务的理想选择。本文将带你从零开始,使用 Rust 的异步栈构建一个完整的 Web API 服务。
为什么选择 Rust 异步?
在深入代码之前,我们先理解 Rust 异步编程的核心优势:
- 零成本抽象:Rust 的异步/await 语法在编译时展开,运行时几乎没有额外开销
- 无畏并发:借用检查器保证异步代码的内存安全,避免数据竞争
- 高性能:基于事件驱动的执行器,单机可轻松处理数十万并发连接
- 现代生态:Tokio 运行时提供了完整的异步 I/O 支持
项目搭建与环境准备
首先确保你已安装 Rust 工具链(1.75+ 版本):
# 创建新项目
cargo new async-web-service
cd async-web-service
# 添加依赖
cargo add tokio --features full
cargo add axum
cargo add serde --features derive
cargo add serde_json
cargo add tracing
cargo add tracing-subscriber
编辑 Cargo.toml 文件,确保配置正确:
[package]
name = "async-web-service"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1.37", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
核心架构设计
我们的 Web 服务将采用分层架构:
┌─────────────────┐
│ HTTP 层 │ ← Axum 路由和处理程序
├─────────────────┤
│ 业务逻辑层 │ ← 异步业务处理
├─────────────────┤
│ 数据访问层 │ ← 数据库/外部API调用
└─────────────────┘
实现步骤
1. 基础服务器搭建
创建 src/main.rs 文件:
use axum::{
routing::{get, post},
Router,
Json,
extract::State,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing_subscriber;
// 应用状态结构
#[derive(Clone)]
struct AppState {
app_name: String,
version: String,
}
// API 响应结构
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
message: String,
}
impl<T> ApiResponse<T> {
fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
message: "Success".to_string(),
}
}
fn error(message: &str) -> Self {
Self {
success: false,
data: None,
message: message.to_string(),
}
}
}
// 健康检查端点
async fn health_check() -> Json<ApiResponse<String>> {
Json(ApiResponse::success("Service is healthy".to_string()))
}
// 用户信息结构
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
username: String,
email: String,
}
// 用户创建请求
#[derive(Debug, Deserialize)]
struct CreateUserRequest {
username: String,
email: String,
}
// 创建用户端点
async fn create_user(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUserRequest>,
) -> Json<ApiResponse<User>> {
tracing::info!(
"Creating user in app: {}, version: {}",
state.app_name,
state.version
);
// 模拟异步操作(实际中可能是数据库插入)
let user = User {
id: 1,
username: payload.username,
email: payload.email,
};
Json(ApiResponse::success(user))
}
// 获取用户列表
async fn get_users() -> Json<ApiResponse<Vec<User>>> {
// 模拟异步数据获取
let users = vec![
User {
id: 1,
username: "alice".to_string(),
email: "alice@example.com".to_string(),
},
User {
id: 2,
username: "bob".to_string(),
email: "bob@example.com".to_string(),
},
];
Json(ApiResponse::success(users))
}
#[tokio::main]
async fn main() {
// 初始化日志
tracing_subscriber::fmt()
.with_env_filter("info")
.init();
// 创建应用状态
let state = Arc::new(AppState {
app_name: "Async Web Service".to_string(),
version: "1.0.0".to_string(),
});
// 构建路由
let app = Router::new()
.route("/health", get(health_check))
.route("/users", get(get_users))
.route("/users", post(create_user))
.with_state(state);
// 启动服务器
let listener = TcpListener::bind("127.0.0.1:3000")
.await
.expect("Failed to bind port");
tracing::info!("Server listening on http://{}", listener.local_addr().unwrap());
axum::serve(listener, app)
.await
.expect("Server failed");
}
2. 添加中间件和错误处理
创建 src/middleware.rs:
use axum::{
http::{Request, StatusCode},
middleware::Next,
response::Response,
Json,
};
use std::time::Instant;
use tracing::info;
// 日志中间件
pub async fn logging_middleware<B>(
req: Request<B>,
next: Next<B>,
) -> Result<Response, (StatusCode, String)> {
let start = Instant::now();
let method = req.method().clone();
let uri = req.uri().clone();
let response = next.run(req).await;
let duration = start.elapsed();
info!(
"{} {} - {}ms - Status: {}",
method,
uri,
duration.as_millis(),
response.status()
);
Ok(response)
}
// 认证中间件
pub async fn auth_middleware<B>(
req: Request<B>,
next: Next<B>,
) -> Result<Response, (StatusCode, Json<serde_json::Value>)> {
// 检查认证头
let auth_header = req.headers()
.get("Authorization")
.and_then(|header| header.to_str().ok());
match auth_header {
Some(token) if token.starts_with("Bearer ") => {
// 验证 token(这里简化处理)
if token.len() > 7 {
Ok(next.run(req).await)
} else {
let error_response = serde_json::json!({
"error": "Invalid token",
"code": "AUTH_ERROR"
});
Err((StatusCode::UNAUTHORIZED, Json(error_response)))
}
}
_ => {
let error_response = serde_json::json!({
"error": "Missing authorization header",
"code": "AUTH_REQUIRED"
});
Err((StatusCode::UNAUTHORIZED, Json(error_response)))
}
}
}
3. 实现异步数据库操作
创建 src/database.rs:
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};
use tracing::error;
use std::time::Duration;
pub struct Database {
pool: Pool<Postgres>,
}
impl Database {
pub async fn new(database_url: &str) -> Result<Self, sqlx::Error> {
let pool = PgPoolOptions::new()
.max_connections(10)
.acquire_timeout(Duration::from_secs(3))
.connect(database_url)
.await?;
// 运行迁移(实际项目中应该使用专门的迁移工具)
Self::run_migrations(&pool).await?;
Ok(Self { pool })
}
async fn run_migrations(pool: &Pool<Postgres>) -> Result<(), sqlx::Error> {
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"#
)
.execute(pool)
.await?;
Ok(())