Actix Web — Rust 上长期占据 TechEmpower 榜首的 web 框架
是什么
Actix Web 是一个用 Rust 写后端服务器的库,主打”又快又类型安全”。日常类比:像一辆手动挡跑车——比 Python/Node 那种自动挡的(FastAPI、Express)要多挂几次档(写类型、处理生命周期),换来的是车快得多。
你用 Actix Web 写一个 Hello World:
use actix_web::{get, App, HttpServer, Responder};
#[get("/hello/{name}")]async fn greet(name: actix_web::web::Path<String>) -> impl Responder { format!("Hello {name}!")}
#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| App::new().service(greet)) .bind(("127.0.0.1", 8080))?.run().await}它跑在 Tokio(Rust 的异步运行时,类似 Node 的事件循环)之上,TechEmpower 这种公开吞吐榜单上常年排前三,2026 年仍然是 Rust 后端的两大主流之一(另一个是 axum)。
为什么重要
不理解 Actix Web,下面这些事都没法解释:
- 为什么”Rust 写 web”在 2017 年之后突然成了一个真选项——以前都说 Rust 难写,是它证明可以
- 为什么 Rust 后端社区分裂成了 actix vs axum 两派——一派要”自由发挥”,一派要”严格泛型”
- 为什么”actor 模型”这个词总被提起又总被绕开——actix 的名字带着它,但日常用法里又没它
- 为什么 Rust 函数 handler 写起来像简单 Express,但报错动辄几十行——背后是类型系统在替你查活儿
核心要点
Actix Web 的设计可以拆成 三件事:
-
多 worker 并行处理请求:HttpServer 启动后按 CPU 核数 spawn 多个 worker 线程(默认 = 核数),每个 worker 都跑一个独立的 App。类比:开了 8 个收银台,每个台都有完整的扫码枪和现金抽屉,互不干扰。
-
强类型 extractor 自动抽参数:handler 的参数类型决定了从请求里取什么。
web::Path<String>取 URL 路径段,web::Query<T>取 querystring,web::Json<T>取 JSON body。类比:你写一张领料单,写”我要 3 个螺丝 + 1 把扳手”,仓库自己按单子配齐,不需要你跑一趟仓库。 -
App 是请求处理的”配置层”:路由、中间件、共享状态都挂在 App 上,每个 worker 启动时各自构造一份。状态如果要共享必须用
web::Data<T>(内部是 Arc)包一下,否则编译器不让你跨 worker。
三件事加起来叫”Actix Web 模型”。
实践案例
案例 1:最小的 GET 路由
use actix_web::{get, App, HttpServer, Responder};
#[get("/hello/{name}")]async fn greet(name: actix_web::web::Path<String>) -> impl Responder { format!("Hello {name}!")}逐部分解释:
#[get("/hello/{name}")]是宏,等同 Express 里app.get('/hello/:name', ...)web::Path<String>这个类型告诉框架:“请把 URL 里{name}那段抽出来当字符串给我”async fn表示它是异步函数,返回impl Responder(任何能转成 HTTP 响应的东西)
比 Go 的 net/http 紧凑(不用手动写 r.URL.Path),比 Express 多了类型保证。
案例 2:JSON API + 共享计数器
use actix_web::{get, post, web, App, HttpServer};use std::sync::Mutex;
struct AppState { count: Mutex<u32> }
#[post("/inc")]async fn inc(data: web::Data<AppState>) -> String { let mut c = data.count.lock().unwrap(); *c += 1; format!("now={}", *c)}逐部分解释:
AppState是自定义状态结构体,里面用Mutex包u32(多 worker 同时访问要加锁)web::Data<AppState>是 actix 提供的”共享句柄”,内部用Arc实现,所有 worker 拿到的是同一份- main 里要
App::new().app_data(web::Data::new(state.clone()))注册一次
这是真实生产代码里 90% 的状态管理写法。
案例 3:JSON 表单自动反序列化
use actix_web::{post, web};use serde::Deserialize;
#[derive(Deserialize)]struct Signup { email: String, password: String }
#[post("/signup")]async fn signup(form: web::Json<Signup>) -> String { format!("registered: {}", form.email)}逐部分解释:
#[derive(Deserialize)]让 serde 库帮 Signup 实现 JSON 反序列化web::Json<Signup>让 actix 自动读 body,按 Signup 形状反序列化,类型不对自动 400
手写解析要 30 行(读 body + serde_json::from_slice + 错误处理),actix 一行搞定。
踩过的坑
-
共享状态忘了 web::Data 包装——直接传
Arc<Mutex<T>>编译器会让你写一堆 trait bound,正确写法是App::new().app_data(web::Data::new(state)),handler 里用data: web::Data<T>接。 -
handler 里跑同步阻塞调用会卡死整个 worker——每个 worker 是单线程事件循环,
std::thread::sleep或std::fs::read大文件都会让该 worker 上所有连接饿死,应该用tokio::fs或把活儿丢给web::block。 -
actor 模型不是日常用法——名字叫 actix 容易误导,新人以为必须懂 actor 才能写路由,其实 4.x 起函数 handler 才是主线,actor crate 只在 WebSocket 长连接才偶尔登场。
-
中间件顺序反直觉——
wrap()是栈式注册,最后注册的中间件最先执行进入请求,容易把日志加在认证之后导致未认证请求没被记录。
适用 vs 不适用场景
适用:
- 需要极限吞吐的内部服务(网关、API 转发、CDN 边缘)——TechEmpower 实测数据可信
- 已经在用 Rust 的团队,想要一个老牌生产级 web 层
- 需要长期支持 HTTP/2、WebSocket、流式响应的场景
不适用:
- 团队没人会 Rust——学习成本(生命周期、async)会让 MVP 推迟一两个月
- 业务还在快速试错、接口天天改——Rust 改类型 + actix 改 extractor 比 Python 改字典慢得多,这种早期项目用 fastapi 或 express 更合适
- 新项目没历史负担、想要更现代 API ——直接选 axum(基于 tower 中间件生态);actix 适合既有 actix 生态的复用
历史小故事(可跳过)
- 2017 年:Nikolay Kim(昵称 fafhrd91)个人项目起步,先做了 actix actor crate(自己写的 Erlang 风格 actor 库,参见 erlang-otp),再在它上面建 actix-web,故得名
- 2018-2020 年:屡次在 TechEmpower benchmark 夺冠,Rust 社区知名度爆发,被认为是”Rust 能写 web”的活证据
- 2020 年:因 unsafe 代码使用引发社区争议,fafhrd91 一度删库退出,社区接手维护,actix-web 4.0 起转为社区驱动,重心从 actor 模型转向函数 handler + extractor
- 2024 年后:axum 凭借 tower 生态和更简洁 API 抢走了不少新项目,但 actix-web 仍是老牌生产级选择
学到什么
- 快不快不只看语言,还看运行时模型——同样 Rust,actix 的”多 worker × Tokio”组合比单线程同步快几十倍
- 类型驱动的 extractor 是 Rust web 框架的核心抽象——一行
web::Json<T>替代了几十行手写解析 - 共享状态要显式 web::Data + 框架名字 ≠ 当前主流用法——比 Express 的 req.app.locals 啰嗦但编译期保证;actix-web 4.x 已经把 actor 推到边缘,新人不必先学 actor
延伸阅读
- 官方文档:actix.rs(教程章节最适合零基础上手)
- GitHub README:actix/actix-web(看一遍 README 就能跑起来)
- 性能对照:TechEmpower Round 22+ actix vs axum 实测
- fastapi —— 思想最像的 Python 框架(也是类型注解 → 自动抽参数)
- express —— Node 老牌框架,对比体会”动态 vs 静态”两条路线
关联
- gin —— Go 高性能 web 框架,定位接近,没 Rust 的类型推导但写法更轻
- fiber —— Go 仿 Express 风格框架,比较”actix 类型重 vs fiber 链式轻”
- fastapi —— Python 上类型驱动 web 的代表,extractor 思路相似
- express —— JS 上动态类型 web 框架的鼻祖,反衬 actix 的”为什么要静态”
- chi —— Go 极简路由库,对比 actix 的”宏 + 类型”重量级风格
- encore —— 一站式 Go 后端框架,对比 actix 的”只管 web 层”
- erlang-otp —— actor 模型的源头,actix 早期借鉴过 OTP 思想
反向链接
- axum —— axum — 用 Rust 类型系统当『路由参数表』的 Web 框架
- chi —— chi — Go 标准库友好的轻量 HTTP router
- deno —— Deno — 安全优先的 JS/TS 运行时
- encore —— Encore — 类型安全 Go/TS 后端框架,基础设施即代码
- erlang-otp —— Erlang OTP — 容错并发系统设计
- express —— Express — Node.js 最经典的 Web 框架
- fastapi —— FastAPI — 用 Python 类型注解写 API
- fiber —— Fiber — 把 Express 写法搬到 Go 上的高性能 web 框架
- gin —— Gin — Go 写 web API 的事实标准框架
- helidon —— Helidon — 让 Java 微服务用同步代码写出反应式性能
- lucia —— Lucia — 主动把自己降级为”学习资源”的 TS 认证库
- matrix-rust-sdk —— matrix-rust-sdk — Matrix 客户端的”共享发动机”
- micronaut —— Micronaut — 编译期搞定 DI 的 JVM 云原生框架
- plug —— Plug — 把 HTTP 中间件写成『conn 进 conn 出』的纯函数
- poem —— poem — 一份 impl 块同时变 HTTP API + OpenAPI 文档站的 Rust 框架
- robyn —— Robyn — Rust 运行时的高性能 Python Web 框架
- rocket —— Rocket — 用 Rust attribute macro 把路由当函数签名写的 web 框架
- salvo —— Salvo — 把中间件和处理器统一成一个 Handler trait 的 Rust web 框架
- slim-framework —— Slim — PHP 圈最轻的 web 框架,专给小 API 用
- spring-boot —— Spring Boot — 用 Auto-configuration 把 Java 后端从 XML 地狱里救出来的事实标准框架
- tide —— Tide — async-std 阵营里 koa 风格的极简 Rust web 框架
- warp —— warp — Rust 里把请求处理拼成 Filter 积木的 web 框架