一、选框架纠结症,让我决定自己测一把
去年团队要起一个新项目,技术选型会上吵了两个小时。有人说用Django,功能齐全;有人说用FastAPI,性能好;还有人说用Spring Boot,虽然重但稳。我听了半天,觉得每个人都在凭感觉和道听途说说话,没一个人真正在同一台机器上跑过对比。
于是会后我干了一件事:把大家提到的几种主流Web框架全装了一遍,每个框架写一个最简单的HTTP接口,返回”Hello World”。然后记录启动时间、内存占用和并发响应。整个过程花了我一个下午加晚上,测完心里就有数了。
我选的五个框架:Python的Flask和FastAPI、Node.js的Express、Java的Spring Boot,以及Go的Gin(虽然Go不是框架但Gin是主流)。都是最新稳定版。机器是我的MacBook Pro M1,16GB内存,系统干净,除了开发工具没跑别的服务。
二、测试方法:尽量公平,但难免有误差
每个框架我都写一个单独的项目,只依赖框架本身,不引入数据库、缓存或其他中间件。接口就一行:return "Hello, World!"或对应的写法。
启动时间:从执行启动命令(比如python app.py或./gradlew bootRun)到终端显示”Listening on port 8000″这类信息为止,我拿手机秒表掐的,每个框架跑了三次取中位数。
内存占用:启动稳定后,用ps aux | grep看RSS(驻留内存)值,取稳定后的读数。
并发测试:我用wrk(一个压测工具)跑30秒并发100连接,看平均延迟和每秒请求数(RPS)。虽然Hello World极简,但能反映框架的基础处理能力。
三、逐一实测,每个都有让我意外的地方
先说Flask。这是Python里最轻量的框架之一。我写了三行代码:
python
复制
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello(): return "Hello, World!"
启动命令flask run,终端显示启动完成大约花了0.8秒,非常快。内存占用22MB,算很低了。wrk压测结果:平均延迟6.2ms,RPS约16000。整个体验很顺畅,没什么毛病。不过Flask默认是同步的,如果你有I/O阻塞操作,性能会明显下降,但Hello World测不出来。对于简单API,Flask的轻量和快速启动是个优势。
然后是FastAPI。最近很火,异步加持。代码稍微多点:
python
复制
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def hello(): return {"message": "Hello, World!"}
启动命令uvicorn main:app,启动时间0.9秒,跟Flask差不多。内存29MB,略高于Flask。wrk压测让我有点意外:平均延迟3.1ms,RPS直接飙到21000。明显比Flask快,异步框架在处理简单请求时确实有优势。但FastAPI默认返回JSON而不是纯文本,我试了改成Response("Hello World")纯文本,延迟降到2.8ms,差别不大。FastAPI启动略慢一丁点,但性能更好,而且自带自动文档,如果项目需要API交互,它比Flask顺手。
再到Express(Node.js)。Node生态里最老牌的框架。代码如下:
javascript
复制
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello, World!'));
app.listen(3000);
启动命令node app.js,启动时间0.4秒,是五个里最快的。内存占用38MB,比Flask和FastAPI都高。wrk压测结果:平均延迟4.8ms,RPS约18000。介于Flask和FastAPI之间。Node的单线程事件循环在处理简单请求时表现不错,但CPU密集型就不行了。Hello World测试中,它的启动速度最快,适合快速原型或需要频繁重启的开发场景。
接下来是Go的Gin。编译型语言的优势在启动时就体现了。我写了:
go
复制
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello, World!")
})
r.Run()
}
我先编译成二进制go build,编译花了1.1秒(比我想象中快),然后执行二进制文件。启动时间0.01秒——真的太快了,秒表按下去还没反应过来就启动了。内存占用1.8MB,低得离谱。wrk压测结果:平均延迟1.2ms,RPS达到48000,把其他几个按在地上摩擦。Gin在性能上完胜,启动时间和资源消耗都是碾压级别。唯一要习惯的是编译步骤,但可以集成到CI里,部署时直接扔二进制就行。
最后是Spring Boot。Java的“重量级选手”。我用了Spring Initializr生成了一个只依赖Spring Web的项目。代码很简单:
java
复制
@RestController
public class HelloController {
@GetMapping("/")
public String hello() { return "Hello, World!"; }
}
启动命令./gradlew bootRun,第一次启动花了7.2秒,包括Gradle初始化,但后续重启因为缓存,大概3.5秒左右。我取的平均是5次后的稳定值,约3.8秒。内存占用直接飙到220MB,比前面四个加起来还多。wrk压测结果倒不差:平均延迟2.3ms,RPS约35000,比Gin慢但比FastAPI快。启动慢、内存大,但运行时性能不弱。Spring Boot适合大型企业级应用,Hello World级的测试完全发挥不出它的优势,但它依然能跑出不错的吞吐量。
四、我把数据整理成了一张表
| 框架 | 语言 | 启动时间(s) | 内存(MB) | 平均延迟(ms) | RPS |
|---|---|---|---|---|---|
| Flask | Python | 0.8 | 22 | 6.2 | 16000 |
| FastAPI | Python | 0.9 | 29 | 3.1 | 21000 |
| Express | Node | 0.4 | 38 | 4.8 | 18000 |
| Gin | Go | 0.01 | 1.8 | 1.2 | 48000 |
| Spring Boot | Java | 3.8 | 220 | 2.3 | 35000 |
看完这表,我第一个反应是:Gin太能打了,几乎全方位胜出。但冷静下来后,我觉得不能只看数据。
五、数据之外的感受
首先,启动时间对Serverless或短生命周期容器很重要。如果每次请求都冷启动,Gin和Express会占优,Spring Boot直接抬走。但如果你的服务是长驻的,启动时间的影响就很小。
其次,内存占用决定了你能在一台机器上跑多少个实例。Spring Boot一个实例220MB,在1GB内存的容器里只能跑3-4个;Gin能跑几百个。如果你的成本敏感,这点很关键。
然后,开发体验是无法量化的。Flask和FastAPI的代码写起来最舒服,Python的动态特性让你改完代码刷新就能看到变化;Spring Boot的依赖注入和AOP虽然重,但大型项目里很管用;Express中间件生态丰富,前端转后端的人上手快;Gin简洁但得习惯Go的静态类型和错误处理。
另外,框架的学习曲线也要考虑。Flask和Express几乎零门槛,FastAPI稍复杂一点因为有异步和类型注解,Spring Boot的注解体系需要时间熟悉,Gin对没写过Go的人有点别扭。
六、我的最终建议
如果你只是写个小工具、内部API或者轻量服务,Gin性能最强,但不一定每个团队都熟悉Go。如果团队熟悉Python,FastAPI是个绝佳选择,性能好、文档自动生成、异步支持,比Flask更适合有一定规模的项目。Flask更适合极简场景或教学用途。Express在Node生态里依然是首选,特别是全栈JavaScript项目。Spring Boot适合大型企业系统、需要事务管理、ORM和大量依赖注入的场景,启动慢但运行稳。
我自己的习惯是:个人项目用FastAPI,公司新项目偏重性能时用Gin,做原型或短期工具用Flask。Spring Boot只在已有Java技术栈的团队里才会选。
这次实测让我明白一个道理:没有哪个框架是“最好的”,只有“最合适的”。Hello World测试能给你一个基线,但真实业务里数据库查询、缓存、网络IO才是真正的瓶颈。所以选框架时,除了跑这个简单的Hello World,最好用你的真实业务逻辑再压一次。不过在那之前,至少你知道每个框架的基础底子怎么样,心里有数总比瞎猜强。
