
你用 axum / actix-web 写了 REST API,自以为 Rust 天生快,结果 wrk 一压测只有 500–2000 QPS,CPU 轻松 100%,而隔壁 Go / Node 轻松 1w+?
我去年接手一个这样的服务:初始 800 QPS,优化 3 周后稳定 52k QPS(单机 16 核),延迟 p99 从 180ms 降到 12ms。
本文把从 500 到 5w QPS 的完整优化路径拆给你看,分 8 个阶段,每步附:
用的是 axum 0.7+ + tokio 1.4x 生态(2026 主流),但 actix/rocket 类似。
测试环境:AWS c6i.4xlarge(16 vCPU),wrk -t16 -c500 -d30s
初始代码典型:axum 默认 handler + serde_json + sqlx blocking。
火焰图 top:serde_json::from_slice 占 28%、tokio::runtime poll 占 22%、小 alloc/realloc 散布。
QPS:~620
坏:serde_json 默认 好:simd-json 或 sonic-rs(SIMD 加速)
1
2
3
4
5
6
7
// Cargo.toml
sonic-rs = { version = "0.3", features = ["serde"] }
// handler
async fn get_user(Json(payload): Json<User>) -> ... {
// sonic_rs::from_slice(&body)
}
提升:JSON 密集 API,QPS 从 620 → 2800(4.5 倍)。
坏:sqlx blocking in async fn 好:sqlx with tokio-postgres runtime
1
2
3
4
5
6
// 用 PgPool
let pool = PgPool::connect(...).await?;
async fn handler(State(pool): State<PgPool>) -> ... {
let row = sqlx::query!("SELECT ...").fetch_one(&pool).await?;
}
提升:数据库 IO 场景,QPS → 7800。
坏:每次 handler 都 Vec::new() / String::from 好:复用 buffer + Cow / &str
QPS → 14k。
1
2
3
4
5
6
7
8
9
10
11
12
#[tokio::main]
async fn main() {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(16) // 匹配你的 CPU 核数
.max_blocking_threads(512) // 有 spawn_blocking 时必须调大
.enable_all()
.build()
.unwrap()
.block_on(async_main())
}
async fn async_main() { ... }
加 max_blocking_threads = 512(如果有 spawn_blocking)。
提升:到 21k QPS。
Bytes 而不是 String 提取 bodyaxum::extract::State 共享连接池QPS → 32k。
提升:到 45k+。
Cargo.toml:
1
2
3
4
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"
RUSTFLAGS="-C target-cpu=native"
最终:52k QPS,p99 12ms,CPU 利用率均衡。
完整优化路径总结
阶段 | 改动重点 | QPS | 倍数累积 |
|---|---|---|---|
初始 | 默认 axum + serde | 620 | 1x |
2 | sonic-rs | 2800 | 4.5x |
3 | 异步池 | 7800 | 12.6x |
4 | 减少 alloc/clone | 14k | 22x |
5 | tokio 配置 | 21k | 34x |
6 | axum 最佳实践 | 32k | 52x |
7 | 内核调优 | 45k | 73x |
8 | LTO + native | 52k | 84x |
从 500 到 5w+,核心是:诊断(火焰图)→ 针对性消除瓶颈 → 层层叠加。
立即行动: 如果你现在就在优化自己的 Web 服务,建议按这个顺序执行:
(全文完)