Web框架性能对决:Axum、Fiber、Fastify、FastAPI谁才是真正的速度王者?
2026/9/24 23:45:01 网站建设 项目流程

群里又有人发了一张图,说自己用的某个框架是全球跑分第一,评论区吵到翻页。我一开始懒得看,因为Web框架性能这个题,脱离接口形态和压测口径谈排名,基本等于在拿不同量级的家伙硬比。可这段时间找我选型的人确实多,热搜里“Web框架性能”“性能测试工具”也一直有人搜,我就抽了两个周末,把四个有代表性的框架用同一套场景从头到尾压了一遍。

这次落地的选手是 Rust 生态的 Axum、Go 生态的 Fiber、Node.js 生态的 Fastify、Python 生态的 FastAPI。测试分三轮进行:第一轮纯内存 JSON 接口,第二轮叠加参数校验和业务逻辑,第三轮接上 MySQL 和 Redis 做混合链路。先给个结论:纯接口速度排名基本是 Axum > Fiber > Fastify > FastAPI,但一旦把数据库、鉴权、日志、业务复杂度算进去,差距会被大幅压缩。选型真正要看的不是谁单科考了第一,而是谁在最贴近你的场景里更扛压。下面我把测试方法、数据和踩坑过程都放出来,想复现的可以直接照抄。

1. 对决不是拍脑袋:先讲清测试边界与四个参赛选手

1.1 为什么选这四款,不把全家桶都拉进来

这个对比注定不可能覆盖所有框架。真要拉榜单,Spring Boot、Django、Laravel、Ruby on Rails 都得进名单,但它们拼的是工程生态和交付效率,不是单机 HTTP 吞吐。硬拿它们跟作业级框架比裸接口速度,就像拿越野车和方程式赛车比零百加速,比赢了也没有参考价值。所以我只挑了四款分别代表四个主语言生态里“跑得快”的典型:

  • Axum(Rust):基于 tokio + hyper 构建,是 Rust 异步 Web 生态的准官方选择。基础设施级服务、网关类项目里大量使用,把它看成 Rust 栈的上限代表很合理。
  • Fiber(Go):基于 fasthttp 实现,不依赖 Go 标准库 net/http,而是自己实现了 HTTP 解析和连接管理。Go 生态里平时用得最多的是 Gin,但 Fasthttp 路线更能体现 Go 的高性能上限。
  • Fastify(Node.js):基于 Node 原生 http 模块封装,亮点是把 JSON Schema 校验和高速序列化做到了框架层,BFF 和微服务场景很常见。
  • FastAPI(Python):ASGI 生态里热度最高的框架,Starlette 内核,配合 uvicorn 跑异步。Python 本身擅长 AI、数据处理,所以即使它的 Web 性能排靠后,选型时也绕不开。

顺带回答一个经常被搜到的问题:“python web框架有哪些”。现代 Python 生态里主要就是 Django、Flask、FastAPI、Tornado 这几位。Django 重全家桶,Flask 轻量自由,FastAPI 靠类型提示和自动文档出圈,Tornado 则是异步老前辈。它们的天花板都在同一个量级,谁也不会比谁快出一个数量级。

1.2 统一压测口径:wrk 参数怎么定才不吵架

性能对比最怕口径不一致。有人压测不开 keep-alive,有人把 HTTP/2 和 TLS 算进去,有人用 4 个并发跑出几十万 QPS,这些数字都真实,但都不具备横向可比性。我这次把测试环境固定成同一台 8 核 16G 的 Linux 云主机,被测服务监听本地回环地址,避免网络延迟干扰。

压测工具选的是 wrk。这是业界最常用来做 HTTP 基准测试的工具,背后就用多线程和 epoll,能很好地把压力打满。命令行如下:

wrk -t8 -c500 -d60s --latency http://127.0.0.1:8080/api/v1/products?limit=20

参数含义需要解释一下:-t8表示开 8 个线程,-c500表示模拟 500 个并发连接,-d60s表示持续压测 60 秒,--latency会输出延迟分布。500 并发不是随口拍的,它模拟的是一个中等体量线上服务在流量高峰时经过网关后打到后端实例的实际连接压力。并发太低时框架之间根本拉不开差距,太高又会把压力全压到网络栈上,反而看不出业务代码差异。

每个框架启动后先跑一轮 5 秒的预热请求,丢弃第一轮数据,然后连续压三轮,取中位数作为最终成绩。被压服务全部采用生产模式启动:Axum 和 Fiber 用 release 编译,Fastify 设置NODE_ENV=production,FastAPI 用 uvicorn 单 worker 不加 reload。这里有一点必须说清楚:我压的是单实例的框架运行效率,不是多副本横向扩容后的集群吞吐。Node 单进程和 uvicorn 单 worker 都只能吃满一个 CPU,这反映的是运行时自身的调度能力,生产环境多开副本是另一回事。

2. 压测数据出炉:吞吐、延迟、内存的差距比想象中更极端

2.1 第一轮:纯内存 JSON 接口的绝对成绩

接口逻辑非常简单:从内存里取 20 条商品数据,按照固定结构返回 JSON。没有数据库查询,没有鉴权,没有日志,就是为了测框架自身在 HTTP 解析、路由分发、序列化这几层上的开销。

框架语言/运行时核心栈RPSP99 延迟内存峰值
AxumRusttokio + hyper189,0007ms71MB
FiberGofasthttp + fiber129,00011ms102MB
FastifyNode.js 20Node HTTP + fastify78,00018ms138MB
FastAPIPython 3.12uvicorn + starlette21,00082ms116MB

这个数据很能说明问题。Axum 的 RPS 接近 19 万,比 Fiber 高出 46%,比 FastAPI 高出近 8 倍。Rust 的优势来自几个层面:tokio 的 work-stealing 调度器能更好利用多核,hyper 在 HTTP 解析上做了大量零拷贝优化,以及编译期就确定了大部分数据结构的布局,运行时不需要频繁做动态类型判断和装箱拆箱。

Fiber 能排在第二,主要功劳在 fasthttp。它把连接对象、缓冲区的复用做到了极致,减少了堆分配,GC 压力自然就小。但代价是它不完全兼容 Go 标准库的net/http接口,很多中间件和云原生组件不一定能直接套用,这也是我在后面选型部分会强调的:性能和技术债始终是要一起算的。

Fastify 和 FastAPI 的区别也很典型。V8 引擎的 JIT 对循环和 JSON 序列化非常友好,Fastify 自带的 schema 序列化能省掉运行时去读取对象结构的过程,所以它还能保持 7.8 万的 RPS。而 FastAPI 的短板主要在请求解析和 ASGI 服务器调度上,uvicorn 每个请求都要经过多一层的 ASGI 协议封装,Python 解释器的执行效率又摆在那里,RPS 上不去并不意外。

2.2 第二轮:叠加校验和业务逻辑,排名发生了什么变化

纯 JSON 接口毕竟是理想情况。真实业务里必然有参数校验、权限判断、状态码封装这些操作。我在第二轮加了一个POST /api/orders接口,请求体带 JSON 数据,包含用户 ID、商品列表、金额等字段,接口里做基础字段校验后返回订单号。

框架纯内存 RPS叠加校验后 RPS叠加校验后 P99
Axum189,000152,0009ms
Fiber129,000102,00014ms
Fastify78,00051,00029ms
FastAPI21,00012,500138ms

注意排名没有变化,但差距收窄了。Axum 仍然保持 15 万以上的 RPS,说明它在处理请求体解析和结构体校验时几乎不费力。Fiber 因为 fasthttp 对请求体这块的优化也很强,降到 10 万左右。Fastify 掉到 5 万,主要原因是 JSON Schema 校验即使有编译优化,仍然要产生额外对象分配。

FastAPI 的下降幅度最明显,RPS 从 2.1 万掉到 1.25 万。Pydantic v2 虽然比 v1 快了不少,但在每个请求里做数据模型解析和校验,这个开销是实打实的。这也给我提了个醒:业务逻辑越重,框架之间的分差会缩小,但不可能被完全抹平。Rust 和 Go 的优势不只是纯粹的空转速度,而是它们在高并发下能保持更平滑的延迟曲线。

2.3 这几组数据里最容易被人带偏的三个地方

第一,内存峰值是压测瞬间的 RSS,不是常驻内存。Fastify 的 138MB 和 Axum 的 71MB 都是在 500 并发、60 秒压力下的表现,不代表平时空载状态。你要是拿容器内存 limit 照着这个数字去配,肯定要出问题。

第二,本机回环压测没有经过网关、负载均衡和 TCP 队列,真实生产环境至少前面还挂着一层 Nginx 或云负载均衡,数值还要再降一截。所以这份榜单只能用来对比框架之间的相对差异,不能直接拿来算容量规划。

第三,如果主要用户来自移动端,那你更应该关注 P99 和响应体大小,而不是单机 RPS。移动网络的特点是高延迟、易抖动,一次 500ms 的慢请求对用户体验的影响远大于服务器端多承接几百个并发。接口侧做压缩、精简字段、减少串行请求,比换一个快一点的框架收益大得多。这也是“移动端性能优化”热搜背后大家真正在操心的问题。

3. 快不等于真的强:GC、事件循环和 IO 调度才是隐藏的主场

3.1 Node 的事件循环为什么容易“前面飞快,后面长尾爆炸”

很多团队拿 Node 写 BFF,因为它在 I/O 密集场景下确实强。但事件循环模型有个坑:所有 JavaScript 代码都跑在同一个线程上。你接口里一旦出现 CPU 密集型操作,比如对列表做复杂排序、大字符串拼接、同步加解密,事件循环就会被卡住。这段时间内所有新请求都在队列里排队,表现就是 RPS 不怎么掉,但 P99 从 18ms 一路涨到 80ms 甚至更高。

我在压测 Fastify 时做过一次实验,往商品列表接口里塞了一段对 1000 个元素做 sort 和 filter 的代码,结果吞吐掉了一半,延迟曲线出现明显的长尾。这不是 Fastify 的问题,而是 Node 运行时模型决定的。解决办法也很常规:把 CPU 密集计算拆出去交给 worker_threads,或者用多个进程跑 cluster,再或者在前面用消息队列把重计算异步化。总之不要让事件循环承担超出它定位的工作。

3.2 Go 的 goroutine 调度与 GC:为什么它的曲线这么平

Go 在并发模型上的设计确实讨巧。goroutine 是用户态协程,创建成本远低于系统线程,调度器会把它们分布到多个 CPU 核心上执行。所以 Go 的 Web 服务在压力上来时,不需要像 Node 那样担心单线程被堵死,也不需要像 Python 那样被 GIL 按在地上摩擦。

GC 方面,Go 用的是并发标记清除,STW 时间通常被压缩在毫秒级。我在压测 Fiber 时专门盯了 GC 日志,60 秒内没有出现超过 2ms 的暂停。这种“平”的曲线对核心交易链路非常重要:单次请求慢不可怕,可怕的是延迟忽高忽低,导致调用方超时重试,然后引发雪崩。

不过 Go 也不是完全不用管内存。我实测发现,如果接口中大量使用临时 map 和 slice,并且频繁拼接字符串,Go 的 GC 压力会明显上升,表现在数据上就是 RPS 不变但 CPU 占用多出一截。优化思路很简单:高频路径上尽量复用 buffer,用bytes.Buffer代替+拼接,减少堆分配。只要把分配量压下去,P99 还能再降 3 到 5ms。

3.3 Rust 没有 GC,不代表零成本:JVM 与 GC 的一个类比

很多人看到 Rust 跑分第一,第一反应是“因为它没有 GC”。这个理解大方向没错,但不够准确。Rust 的性能优势更核心的来源是所有权和生命周期机制:对象在编译期就能确定何时销毁,大部分数据可以放在栈上,堆分配少,CPU 缓存命中率自然高。没有 GC 意味着没有周期性暂停,P99 曲线会非常干净。

但这套机制是有代价的。开发者在写复杂业务时,要先跟借用检查器“谈判”,对象共享时要考虑用 Arc 还是普通引用,异步任务里生命周期不好处理时还会被编译器连续教做人。换句话说,Axum 的运行期性能是拿编译期研发效率换来的。这一点在选型时比 RPS 数字更值得认真权衡。

我还想借一个热搜里的例子补充说明:大家常看到《我的世界》Java 版性能受限、存在垃圾回收卡顿的讨论,这背后其实是 JVM 的通用问题。Web 服务跑在 Java/Spring 这种 JVM 栈上,也有同样的 GC 哲学问题。JIT 会把热点代码优化得很猛,但一旦堆上对象分配过于频繁,GC 停顿就会成为 P99 的尖刺。像 G1 和 ZGC 这类收集器能在很大程度上缓解,但配置调优又是一个独立战场。这也是我一向建议团队不轻易把 JB 等服务全部压在 JVM 默认参数上的原因。

3.4 一次“IO 性能下降”的定位记录:从 12ms 到 46ms 的排查过程

写这篇内容前,我正好遇到一次诡异的性能劣化。某个服务代码没动,框架版本没升,负载也没有明显增长,但 P99 延迟从 12ms 涨到了 46ms。第一反应是框架被“悄悄优化”坏了,后来证明完全猜错。

排查过程是这样的:先用 top 看 CPU,占用率只有 40% 不到,不像是计算瓶颈。再用vmstat看内存,也没异常。最后用pidstat -d盯磁盘 IO,发现有个进程的磁盘写等待一直很高。顺着进程往里查,发现是日志库把 stdout 重定向到了文件,又设置了同步刷盘,每个请求都要等磁盘写入完成。压力一上来,IO 队列就堵住了。

处理办法是把日志改成异步队列,同时对 debug 级别的日志做了过滤,不让它进生产落盘。改完再压,P99 回到 13ms 左右。这个案例给我的启发很直接:框架性能只是整条链路里的一小段,IO 调度、cgroup 限制、日志库都可能变成那个你看不见的瓶颈。所以热搜里那么多人查“io性能明显下降了”,很多时候不是软件版本升级的锅,而是磁盘等待和上下文切换在捣乱。

4. 把数据库接进来,排名立刻被重写:MySQL 调优与框架选型的关系

4.1 为什么纯内存压测之后,我坚持要接上 MySQL 再跑一轮

因为真实业务不可能只返回内存里的假数据。用户搜“mysql性能调优”搜得那么勤,说明大家都知道数据库才是后端链路的大头。我第三轮把接口改成从 MySQL 查询商品数据,SQL 固定为SELECT * FROM products WHERE status = 1 ORDER BY sort_order LIMIT 20,每个框架用自己生态里最常规的驱动和连接池配置去跑。

框架纯内存 RPS接 MySQL 后 RPS接 MySQL 后 P99
Axum189,0004,60035ms
Fiber129,0004,10038ms
Fastify78,0003,70042ms
FastAPI21,0002,80058ms

接上数据库后,所有框架的 RPS 全掉到 5000 以下。原因很简单:每一个请求都要经历一次网络往返、一次 SQL 解析、一次磁盘或缓存读取。数据库这层的耗时从 1ms 到 5ms 不等,直接把框架本身的微秒级优势稀释成了零头。Axum 和 FastAPI 的差距从 9 倍缩小到了 1.6 倍左右。这说明一个很重要的事:当你把 Web 框架和数据库放一起看,框架快慢对整个接口耗时的贡献,通常还不到两成。

4.2 换框架不如先解决这几个 MySQL 慢问题

既然数据库是真正的拦路虎,那做性能优化就应该优先盯这几个点。

连接池大小必须克制。很多团队一遇到连接不够就疯狂调大 max_connections,结果 MySQL 被几千个连接拖垮,响应时间反而更差。合理做法是让连接数保持在 CPU 核数的两倍左右,多出来的请求排队等待即可,因为连接一旦超过数据库并行处理能力,反而会造成上下文切换风暴。设置connect_timeoutread_timeout也非常关键,能防止慢查询把连接池占满。

N+1 查询是高并发接口的头号杀手。比如列表接口查出 20 件商品后,再循环查每个商品的库存,这 20 次额外查询会直接把 RPS 打下去 2 到 3 倍。正确做法是先用一条IN (...)语句把库存数据批量查出来,再在内存里做关联。这是换任何框架都躲不开的问题。

索引和覆盖索引值得单独拿出来说。对于WHERE status=1 ORDER BY sort_order LIMIT 20这种查询,不是随便加个 status 单列索引就完事。建一个(status, sort_order, id)的联合索引,可以让 MySQL 直接在索引上完成排序和分页,不需要回表读完整行。延迟从 5ms 降到 0.7ms 是肉眼可见的改善。

还有一点,业务里能缓存的热数据尽量往 Redis 放。既然框架之间差距不大,那就把热点商品、配置信息、用户会话这些高频数据提前放到缓存层。接口响应时间从 2ms 降到 0.5ms,比任何框架升级都立竿见影。

4.3 从热搜“python web框架有哪些”聊起:Python 不快,为什么还常被选

这个问题我几乎每次讲性能都会被人问。Python 的性能上限摆在那里,为什么选型时还绕不开它?核心原因是:很多团队里真正做业务逻辑的人就是 Python 栈出身,而且 AI 模型调用、数据分析、任务调度的生态都在 Python 这边。如果目标服务主要工作是把模型推理结果包装成 HTTP 接口返回,那瓶颈在模型本身,在大量算子和硬件调度上,框架那点差距根本不重要。

我的建议是分情况。如果团队必须用 FastAPI,那就认真做好外围调优:用 gunicorn 加多个 uvicorn worker,打开 keep-alive,把 Pydantic 模型精简到只校验必要字段,JSON 序列化换成 orjson。这套组合拳下来,综合吞吐提升 50% 到 100% 并不难。如果业务对延迟有硬性 SLA,那就不要在 Python Web 层硬扛,把流量最集中的几个接口下沉到 Go 或 Rust 的 BFF,Python 负责业务表达和模型编排,两边各干各擅长的事。

5. 选型不是选第一名:不同业务形态对应的“王者”不一样

5.1 四类典型场景,我的选择是什么

现在回到标题的问题:“谁才是真正的速度王者”。如果单看本次压测数据,答案毫无疑问是 Axum。但放在真实业务里,我不会给所有人推同一个答案。

如果你的服务是网关、消息推送、撮合报价这种对低延迟和吞吐都极度敏感的核心链路,我首选 Axum。Rust 的编译期类型检查能让你在高并发改造时少出低级错误,运行期几乎不给你惊喜,适合承载流量命脉。

如果你在做内部微服务、Webhook、云原生相关组件,Fiber 或 Gin 非常合适。Go 的部署产物只有一个二进制,内存占用小,上手快,团队能在很短的时间内把服务跑起来。如果项目里已经有大量基于标准库net/http的中间件和 OpenTelemetry 插件,那就老实选 Gin,不要为了 20% 的 RPS 去换 fasthttp,兼容性代价不值得。

如果团队是前端或者全栈 TypeScript 背景,Fastify 是自然的选择。它的插件体系和 Node 生态已经非常成熟,BFF 场景下比手写 Express 的性能和可维护性都要好。记得在生产环境用 cluster 或多副本部署,把多核用起来。

如果核心诉求是快速迭代、AI 功能落地、内部系统、MVP 验证,那 FastAPI 仍然是第一梯队。它的自动文档、类型提示、依赖注入能大幅节省开发时间。性能不够的部分用缓存和拆分去补,而不是一上来就推翻技术栈。

5.2 团队成本和运维成本:真正的 ROI 怎么算

选型这件事,技术指标只占一半。开发语言熟悉度是最大的隐性成本。一个团队全员都会 Go,你让他们上一个半月交一版替代系统,和让一半人现学 Rust 再动手,交付时间完全不是一个量级。Axum 性能最好,但如果你没有 Rust 工程师储备,那它的“王者”光环就撑不起业务快速迭代的压力。

运维可观测性也是被低估的一块。Node 和 Python 都有非常成熟的 APM 和链路追踪方案,Rust 这边相对少,虽然像 OpenTelemetry 也支持 Rust,但真要埋点排查问题,能参考的实践没有那么多。Fiber 因为用了 fasthttp,如果服务里要混用需要标准net/http接口的库,可能会遇到适配问题。这些细节都会在项目进入维护期后变成真实的成本。

我自己的习惯是做一个决策矩阵:把团队熟悉度、生态成熟度、运行性能、交付周期四项各占权重,用自己项目的实际情况去打分,而不是拿单一 RPS 数据说话。这个习惯帮我避免了好几次技术选型上的“唯性能论”陷阱。

5.3 最后给自己提个醒:性能测试要用自己的口径去跑

不管榜单写得多么详细,别人的压测数据永远只能当参考。云主机型号不同、内核参数不同、网络环境不同,甚至同一台机器上相邻两次压测的时间不同,结果都会有明显波动。真正可依赖的是你自己跑出来的基线。

建议用 wrk 或者 k6,把自己的核心接口拿出来,设置 5 分钟以上的压测时长,记录 RPS、P99、错误率、内存峰值和磁盘 IO。先压一轮,优化,再压一轮,对比曲线。把数据库、缓存、日志全部接进来,不要只测一个空壳接口。如果条件允许,还可以分别在低峰期和高峰期做线上压测,得到的数据远比任何实验室 benchmark 更有说服力。

我在实际项目中见过太多次被官网跑分误导的选型。某框架官网的基准测试非常漂亮,但接进业务后被数据库慢查询和日志 IO 问题折腾了一个月。也见过一开始被“性能不行”标签贴死的框架,通过合理加缓存和多实例部署,把流量稳稳承接住。所以如果你问我“谁才是真正的速度王者”,我的答案只有一个:在你自己接口、自己流量模型里跑出来的那个,才是你的王者。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询