Rust vs Go Web 框架性能对比:Axum、Actix、Gin、Fiber 的 10 万并发压测报告
一、技术选型的务实困局:语言性能差距到底被夸大了多少
在决定新项目的 Web 框架技术栈时,Rust 和 Go 是两个最终的候选者。Go 的支持者列举 Gin/Fiber 的生态成熟度和开发效率,Rust 的支持者则用 Techempower 的 Benchmark 数据强调 Axum/Actix 的极致性能。双方引用的数据来源相同,但结论截然相反。
表面的分歧来自于 Benchmark 设计的不等价性。Techempower 的测试场景(纯文本 "Hello World" 返回)与真实业务负载(JSON 序列化、数据库查询、中间件链)之间横亘着一道巨大的鸿沟。正确的做法是:在团队的实际业务 Profile 下,同口径对比,让数据消除偏见。
二、压测环境与结果
硬件环境为 16C32G 物理机,使用 wrk2 做恒定速率压测,每个场景持续 60 秒。框架版本为 Axum 0.7、Actix-Web 4、Gin 1.9、Fiber 2.50。
场景 1:静态纯文本路由
| 框架 | QPS | P50 | P99 | 内存 | CPU |
|---|---|---|---|---|---|
| Actix-Web | 680,000 | 0.2ms | 1.8ms | 45MB | 95% |
| Axum | 520,000 | 0.4ms | 2.5ms | 52MB | 92% |
| Fiber | 380,000 | 0.6ms | 4.2ms | 68MB | 88% |
| Gin | 280,000 | 1.0ms | 6.8ms | 85MB | 82% |
在极限吞吐场景下,Actix-Web 几乎领先 Fiber 2 倍。但这是纯粹的"空跑"测试,与任何实际业务都无关。
场景 2:JSON API(接收 500B JSON,序列化后返回 2KB JSON)
| 框架 | QPS | P99 | 内存 | 序列化开销占比 |
|---|---|---|---|---|
| Actix-Web | 180,000 | 3.2ms | 89MB | 32% |
| Axum | 145,000 | 4.5ms | 95MB | 38% |
| Fiber | 105,000 | 8.5ms | 120MB | 52% |
| Gin | 72,000 | 15ms | 145MB | 58% |
有了 JSON 序列化后,差距从 2 倍缩小到约 2.5 倍。但值得注意的是,序列化开销占比的不同解释了差距的缩小——Go 的encoding/json使用反射,开销远高于 Rust 的serde_json基于宏的零成本抽象。
场景 3:中间件链(鉴权 JWT 验证 + 令牌桶限流 + 结构化日志)
| 框架 | QPS | P99 | 中间件总开销 |
|---|---|---|---|
| Actix-Web | 95,000 | 8.5ms | 1.2ms |
| Axum | 78,000 | 11ms | 1.5ms |
| Fiber | 48,000 | 22ms | 4.8ms |
| Gin | 32,000 | 38ms | 7.2ms |
中间件层越重,Go 框架的劣化越明显。原因在于每个中间件的c.Next()调用都在 goroutine 栈上产生额外的函数调用帧,Rust 的编译期内联消除了这个开销。
三、开发效率与维护成本的隐性权衡
性能数据之外,四个框架的开发体验差异同样关键:
// Gin:最直观的中间件注册模式 func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("Authorization") claims, err := validateJWT(token) if err != nil { c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"}) return } c.Set("claims", claims) c.Next() } } func main() { r := gin.Default() r.Use(AuthMiddleware()) r.GET("/api/data", handleData) r.Run(":8080") }// Axum:类型安全但更长的编译反馈循环 async fn auth_middleware( headers: HeaderMap, mut req: Request, next: Next, ) -> Result<Response, StatusCode> { let token = headers.get("authorization") .ok_or(StatusCode::UNAUTHORIZED)? .to_str() .map_err(|_| StatusCode::UNAUTHORIZED)?; let claims = validate_jwt(token)?; // ? 自动传播错误 req.extensions_mut().insert(claims); Ok(next.run(req).await) } #[tokio::main] async fn main() { let app = Router::new() .route("/api/data", get(handle_data)) .layer(from_fn(auth_middleware)); // 类型安全的中间件层 // 编译期保障:如果中间件的输入/输出类型不匹配,编译失败 let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); axum::serve(listener, app).await.unwrap(); }开发效率的对比(基于同一功能模块的实现工时统计):
| 维度 | Go (Gin) | Rust (Axum) |
|---|---|---|
| 基础 CRUD API | 2h | 3.5h |
| 数据库集成 (SQLx) | 1.5h | 2h |
| 中间件开发 | 1h | 2h |
| 编译时间(首次) | 3s | 28s |
| 编译时间(增量) | 1.5s | 8s |
| 单测编写效率 | 高(testify) | 中(需处理异步) |
| 类型安全 bug 引入率 | 1.2 次/周 | 0.08 次/周 |
Rust 在开发效率上存在约 1.5~2 倍的工时差异,但其类型系统和所有权模型在运行时错误预防上的价值随着服务规模增长而放大。
四、选型矩阵与决策建议
综合性能、开发效率、生态和维护成本四个维度:
| 维度 | Actix-Web | Axum | Fiber | Gin |
|---|---|---|---|---|
| 原始性能 | ★★★★★ | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ |
| 类型安全 | ★★★★★ | ★★★★★ | ★★☆☆☆ | ★★☆☆☆ |
| 开发效率 | ★★☆☆☆ | ★★☆☆☆ | ★★★★★ | ★★★★★ |
| 生态成熟度 | ★★★★☆ | ★★★★☆ | ★★★☆☆ | ★★★★★ |
| 维护成本 | ★★★☆☆ | ★★★☆☆ | ★★★☆☆ | ★★★★☆ |
五、总结
Rust vs Go Web 框架选型的决策原则:
- 性能差距被中间件层放大:纯路由场景 Actix 领先 Gin 2.4 倍,加入业务中间件后差距扩大到 3 倍。路由性能不是决策重点,中间件链的开销才是;
- Go 的序列化是真正的瓶颈:
encoding/json基于反射的实现是 Go Web 框架性能的最大单一瓶颈,启用sonic或jsoniter替代可将 JSON API 的 QPS 提升 40~80%; - Rust 的编译时间是开发体验的最大摩擦点:增量编译 8s vs Go 的 1.5s,在快速迭代阶段这种差异会累积为显著的等待焦虑;
- 类型系统带来的安全性差异被严重低估:Axum 的类型安全中间件链可以在编译期捕获约 30% 的运行时间类型错误,在服务规模达 50+ 微服务时这是一笔可观的维护成本节省。
推荐路径:核心网关和延迟敏感型服务选 Rust + Axum,业务服务选 Go + Fiber(配合 sonic 序列化)。不要试图用一个语言打满全场。