如何暴露 REST API?
解读
在国内后端面试中,“如何暴露 REST API”并不是问“写个 Hello World 就行”,而是考察候选人能否从零到生产把一套 Rust HTTP 服务落地:
- 技术选型是否符合国内主流云原生场景(K8s、阿里云、腾讯云);
- 代码是否内存安全、并发安全且性能可预测;
- 是否具备可观测性(日志、指标、追踪)与灰度发布意识;
- 是否了解Rust 特有的异步运行时、线程模型与编译期检查对 REST 的影响。
一句话:让面试官相信你能用 Rust 写出可上线、可维护、可水平扩展的 REST 服务。
知识点
- 协议层:HTTP/1.1、HTTP/2、TLS 1.3、ALPN、QUIC(国内云厂商已支持 HTTP/3 内网加速)。
- 框架层:
- 同步:actix-web(国内岗位 JD 出现频率最高)、rocket(编译期宏多,适合快速原型)。
- 异步:axum(tokio 官方出品,零成本抽象,与 tonic 无缝混用)、poem(国产框架,对 OpenAPI 友好)。
- 序列化:serde + serde_json,bincode 用于内网高性能网关,protobuf 用于 BFF → 微服务通信。
- 中间件:CORS、压缩(gzip/br)、限流(基于 token bucket)、鉴权(JWT + JWK 动态拉取)、Trace(opentelemetry-rust + 阿里云 SLS Jaeger)。
- 并发模型:tokio work-stealing 调度器,默认线程数 = CPU 核心数,!Send 类型禁止跨 await 编译期报错。
- 内存与性能:零拷贝(bytes::Bytes)、hyper body stream、jemalloc 在 4 核 8 G 容器下比默认 malloc TPS 提升 8–12%。
- 部署:静态链接 musl 生成单文件 8 MB 镜像,distroless/cc 基础镜像减少 CVE 扫描告警;Kubernetes + HPA 基于 QPS 与 P99 延迟双指标弹性。
- 编译期保证:
- 借用检查防止悬垂指针泄漏到响应;
- 'static 约束避免将局部引用塞进 tokio spawn;
- Send + Sync 边界让并发 bug 在编译期暴露。
答案
以下示例基于 axum 0.7 + tokio 1.40,演示国内最常见的“商品模块” REST 接口,涵盖分层架构、错误处理、OpenAPI 文档、可观测性,可直接放进简历项目。
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing::{info, instrument};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// 1. 领域模型
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Product {
id: u64,
name: String,
price: u64, // 分为单位,避免浮点精度
}
// 2. 错误类型:编译期保证 Send + Sync
#[derive(Debug)]
enum AppError {
NotFound,
BadRequest,
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (code, msg) = match self {
AppError::NotFound => (StatusCode::NOT_FOUND, "resource not found"),
AppError::BadRequest => (StatusCode::BAD_REQUEST, "invalid param"),
};
(code, msg).into_response()
}
}
// 3. 共享状态:连接池、配置、缓存
#[derive(Clone)]
struct AppState {
pool: Arc<Vec<Product>>, // 简化成内存 Vec,生产用 sqlx::Pool
}
// 4. 业务逻辑层
#[instrument(skip(state))]
async fn get_product(
Path(id): Path<u64>,
State(state): State<AppState>,
) -> Result<Json<Product>, AppError> {
state
.pool
.iter()
.find(|p| p.id == id)
.cloned()
.map(Json)
.ok_or(AppError::NotFound)
}
#[instrument(skip(state))]
async fn create_product(
State(state): State<AppState>,
Json(payload): Json<CreateProduct>,
) -> Result<Json<Product>, AppError> {
let new = Product {
id: fastrand::u64(1..=1_000_000),
name: payload.name,
price: payload.price,
};
// 生产用 write-ahead log + 幂等键
Ok(Json(new))
}
#[derive(Deserialize)]
struct CreateProduct {
name: String,
price: u64,
}
// 5. 路由组装
fn api_route() -> Router {
let state = AppState {
pool: Arc::new(vec![
Product {
id: 1,
name: "Rust 权威指南".into(),
price: 8900,
},
]),
};
Router::new()
.route("/products/:id", get(get_product))
.route("/products", post(create_product))
.with_state(state)
.layer(tower_http::trace::TraceLayer::new_for_http()) // 自动 TraceId
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 6. 日志与追踪:对接阿里云 SLS
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
))
.with(tracing_subscriber::fmt::layer())
.init();
let app = api_route();
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
let listener = TcpListener::bind(addr).await?;
info!("listening on {}", addr);
axum::serve(listener, app).await?;
Ok(())
}
关键亮点
- 编译期保证:
AppError自动实现Send + Sync,跨 await 安全;State使用Arc共享,无显式锁。 - 零成本抽象:
axum的Router在编译期展开为状态机,无动态分发。 - 可观测性:
TraceLayer自动生成 TraceId,符合国内金融监管要求;生产可替换为opentelemetry-otlp直推阿里云链路追踪。 - 灰度发布:通过
x-mse-tag头 +tower::load_shed实现按用户百分比灰度,无需改业务代码。
拓展思考
- 性能调优:
- 在 4 核 8 G 容器内,调整 tokio worker_threads = 4,hyper max_buf_size = 256 KB,可将 P99 延迟从 12 ms 降到 7 ms;
- 使用 mimalloc 替代 jemalloc,在 ARM 国产芯片(鲲鹏 920)上 TPS 再提升 5%。
- 安全加固:
- 启用 ring 提供的 TLS 1.3 only,关闭 0-RTT 防止重放;
- 使用 cargo-audit 在 CI 阶段阻断 RUSTSEC 漏洞,国内银行外包项目已强制要求。
- 多协议混用:
- 同一端口同时暴露 REST 与 gRPC:axum + tonic,HTTP/2 多路复用节省 30% 云厂商带宽费;
- 通过 content-type negotiation 实现 json/proto 双协议,Android 老客户端无需升级。
- Serverless 场景:
- 编译为 wasm32-wasi,在阿里云函数计算 Custom Runtime 冷启动 40 ms,比 Node.js 冷启动快 3 倍;
- 利用 cargo-lambda 直接生成 AWS Lambda zip,国内出海业务一键部署。
- 团队协同:
- 使用 utoipa 自动导出 OpenAPI 3.1,前端同学通过 yapi 导入即可 mock;
- rustfmt + clippy + pre-commit 强制在 MR 阶段通过,华为 Rust 编程规范已将其写入红线。