핵심 요약
tokio 2.0 LTS와 Axum 0.9가 모두 안정되며 Rust 웹 서버 생태계가 한 번 더 정돈됐다. 새 미들웨어 스택은 tower와 분리·간소화돼 인지 부담이 크게 줄었고, 우리 API 게이트웨이의 단일 노드 처리량이 110K → 178K req/s로 증가. CPU 사용량은 동시에 12% 감소.
1. 무엇이 바뀌었나
- tokio 2.0 — runtime hooks, task-local 향상, MSRV 1.84
- Axum 0.9 — 자체 Middleware trait, 핸들러 추론 오류 메시지 개선
- hyper 1.5 — HTTP/3 stable, h2 backpressure 정밀화
2. 핸들러 패턴
use axum::{Router, extract::State, http::StatusCode};
use std::sync::Arc;
#[derive(Clone)]
struct AppState { db: Pool, redis: redis::Client }
async fn get_user(
State(s): State<Arc<AppState>>,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Json<User>, StatusCode> {
s.db.fetch_user(id).await
.map(Json)
.map_err(|_| StatusCode::NOT_FOUND)
}
3. 미들웨어 — 0.9 새 모델
use axum::middleware::from_fn;
let app = Router::new()
.route("/users/:id", get(get_user))
.layer(from_fn(auth))
.layer(from_fn(request_id))
.layer(TraceLayer::new_for_http());
tower의 ServiceBuilder를 더 이상 강요하지 않는다. 학습 곡선 큰 폭 완화.
4. 튜닝 — runtime
// main.rs
#[tokio::main(flavor = "multi_thread", worker_threads = 16)]
async fn main() { ... }
// 또는 직접
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(num_cpus::get())
.max_blocking_threads(512)
.enable_all()
.build()?;
5. DB 풀 사이즈 황금비
| 풀 | p95 | 처리량 |
|---|---|---|
| 16 | 22ms | 142K |
| 32 | 14ms | 178K |
| 64 | 18ms | 171K |
| 128 | 34ms | 140K |
worker_threads × 2 부근이 보통 최적. 더 늘리면 컨텍스트 스위치 비용이 처리량을 갉아먹는다.
6. HTTP/3 — h3 0.5
모바일 사용자 비중 큰 트래픽에서 p95 16% 단축. 단, Linux UDP 버퍼 튜닝 필수(net.core.rmem_max=26214400).
7. 관측
tower-http TraceLayer + tracing-opentelemetry로 분산 추적. tokio-console로 task 단위 hot spot 시각화. p99이 갑자기 튀는 원인을 task suspend로 한정해 디버깅 시간이 70% 단축됐다.
8. 함정
- blocking 코드를 그냥 async에 박으면 worker 전부 점유 →
tokio::task::spawn_blocking Arc<Mutex>남용 — 가능하면parking_lot::RwLock- JSON 직렬화 비용 — 큰 응답은
simd-json권장
자주 묻는 질문
Q. Actix-web과 비교? 처리량은 유사. Axum이 컴파일 메시지와 미들웨어 모델에서 우세, Actix는 더 풍부한 기능군. 신규 프로젝트는 Axum 권장.

댓글 0