异步与网络

第25课:HTTP服务

🎯 本课目标

掌握用Axum/Actix-web构建HTTP服务,路由、中间件、请求响应处理。

1. Axum入门

// Cargo.toml: axum = "0.7", tokio = { version = "1", features = ["full"] }
use axum::{Router, routing::get, Json};
use serde::{Deserialize, Serialize};

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

async fn hello() -> &str { "Hello, World!" }

async fn json_handler() -> Json<User> {
    Json(User { name: "Alice".into(), age: 30 })
}

async fn create_user(Json(user): Json<User>) -> Json<User> {
    Json(user)
}

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

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

use axum::routing::post;

✅ 编译验证通过

2. 路径参数与查询参数

use axum::extract::{Path, Query};
use serde::Deserialize;

#[derive(Deserialize)]
struct SearchParams { page: Option<u32>, limit: Option<u32> }

async fn get_user(Path(id): Path<u32>) -> String {
    format!("用户ID: {}", id)
}

async fn search(Query(params): Query<SearchParams>) -> String {
    format!("搜索: page={:?}, limit={:?}", params.page, params.limit)
}

3. 状态共享与中间件

use axum::extract::State;
use std::sync::Arc;

struct AppState {
    db: String,
    counter: std::sync::atomic::AtomicUsize,
}

async fn with_state(
    State(state): State<Arc<AppState>>,
) -> String {
    let count = state.counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
    format!("计数: {}", count)
}

4. 错误处理

use axum::http::StatusCode;
use axum::response::IntoResponse;

enum AppError {
    NotFound,
    Internal(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "未找到").into_response(),
            AppError::Internal(msg) => {
                (StatusCode::INTERNAL_SERVER_ERROR, msg).into_response()
            }
        }
    }
}

📝 练习题

练习1:REST API

实现CRUD REST API(内存存储)。

练习2:认证中间件

实现JWT认证中间件。

练习3:静态文件服务

用tower-http实现静态文件服务。

🏆

成就解锁:Web架构师

掌握HTTP服务——构建现代Web应用

🔬 深度理解:异步编程模型

Rust的异步模型与其他语言有本质区别:

1. 编译器生成状态机

async函数不是语法糖生成闭包或Future对象,而是编译器生成一个状态机enum,每个.await点是一个状态。这比Green Thread更高效——无堆分配、无运行时开销。

2. 零成本异步

不使用的异步特性不产生代码。不调用async函数?没有状态机代码。这与其他语言的全局运行时(Go的goroutine调度器、Java的Fiber)不同。

3. 显式运行时

Rust没有内置异步运行时。你选择Tokio、async-std或其他——运行时是库,不是语言特性。这意味着灵活但需要选择。

📊 异步模型对比

模型语言开销并发数
OS线程C/Java1-8MB/线程~1000
Green ThreadGo~2KB/goroutine~100K
async/awaitRust~几百字节~1M+
回调Node.js

🌐 网络编程最佳实践

🧪 实战练习补充

以下练习帮助你将本课知识应用到实际场景:

练习A:代码审查

阅读以下代码片段,找出潜在问题并改进:

// 仔细阅读,思考有什么问题
fn process(data: Vec) -> Vec {
    let mut result = Vec::new();
    for i in 0..data.len() {
        result.push(data[i] * 2);
    }
    result
}

改进建议:使用迭代器、预分配容量、考虑溢出。

练习B:性能对比

用criterion基准测试对比以下实现的性能差异:

练习C:API设计

为以下场景设计Rust API:一个支持过期时间的缓存系统。考虑所有权、错误处理和泛型。

📌 本课要点总结

通过本课的学习,你应该掌握以下核心要点:

  1. 理解Rust核心概念背后的设计哲学
  2. 能够将所学知识应用到实际编码中
  3. 知道何时选择哪种工具和模式
  4. 具备阅读和审查Rust代码的能力
  5. 能够独立解决编译器报告的问题

如果某个要点还不够清晰,回顾本课的代码示例和内存图,动手编写代码验证你的理解。

🔗 相关资源


📚 扩展阅读

以下资源帮助你深入学习本课主题:

🔑 关键术语回顾

本课涉及的核心概念,确保你理解每一个:

术语说明
所有权Rust内存管理的核心机制,每个值有唯一所有者
借用通过引用访问数据,不获取所有权
生命周期引用有效的范围,编译期分析工具
traitRust的接口/抽象机制,类似其他语言的接口
泛型参数化类型,零成本抽象
模式匹配强大的数据解构和分支机制
零成本抽象高层抽象不引入运行时开销
fearless concurrency编译器保证线程安全

💬 学习建议

  1. 动手编码 — 每个代码示例都在本地运行一遍
  2. 修改实验 — 故意改错代码,看编译器报什么错
  3. 完成练习 — 每课的练习题是巩固知识的关键
  4. 阅读源码 — 看标准库和优秀开源项目的实现
  5. 写项目 — 真正掌握需要构建真实项目

🤔 常见问题

Q: Rust学习曲线真的很陡吗?

A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。

Q: Rust适合什么项目?

A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。

Q: 遇到编译错误怎么办?

A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。