如何以公平、隔离的方式对 Web 框架进行基准测试 | Mahdi Shamlou
Source: Dev.to
大家好!我是 Mahdi Shamlou 👋
我在网上看到很多比较 Web 框架的帖子,但它们大多要么有偏见、要么已经过时、要么难以复现。所以我想分享一种实用的方式来基准测试任何 Web 框架,保持完全隔离、公平且可复现。
我们将使用 Docker 进行隔离,使用 k6 进行负载测试,并以 Python 框架——FastAPI 和 Flask——作为简单示例。这种方法同样适用于 Node.js、Go、Java、Rust 或其他任何语言。
概览
对 Web 框架进行基准测试可能很棘手。许多因素会影响结果:
- CPU 与内存可用性
- 工作进程 / 线程数量
- 背景进程
- 路由、日志、数据库、I/O
为了进行公平的比较,你需要:
- 使用固定 CPU 与内存限制的 Docker 容器
- 在每个框架中使用相同的路由或端点
- 使用 k6(或类似工具)进行受控的负载测试
- 将结果保存以便后续分析
项目结构
mkdir web_framework_benchmarks
cd web_framework_benchmarks
mkdir framework1 framework2 k6-tests results
您可以将 framework1 和 framework2 替换为您想比较的任何框架。演示中我们使用一个简单的 /hello 端点。
FastAPI(Python)
# app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello():
return {"message": "hello world"}
Flask(Python)
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/hello")
def hello():
return jsonify({"message": "hello world"})
您可以在 Node.js、Go、Java 等语言中实现相同的端点,保持功能完全一致。也可以选择添加一个 sleep 路由来模拟 I/O 密集型工作。
Dockerfiles(公平比较)
FastAPI Dockerfile
# Dockerfile (FastAPI)
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install fastapi uvicorn gunicorn
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-w", "1", "-b", "0.0.0.0:8000", "app:app"]
Flask Dockerfile
# Dockerfile (Flask)
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask gunicorn
CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "app:app"]
✅ 两个容器现在拥有相同的工作进程数量和相同的 CPU/内存 限制,确保了公平的基准。
负载测试 k6
k6 脚本 (JavaScript)
// k6-tests/framework1.js (or framework2.js)
import http from "k6/http";
import { sleep } from "k6";
export const options = {
stages: [
{ duration: "30s", target: 50 }, // ramp‑up
{ duration: "1m", target: 200 }, // hold load
{ duration: "30s", target: 0 }, // ramp‑down
],
thresholds: {
"http_req_duration": ["p(95)<200"], // 95% of requests should be <200 ms
},
};
export default function () {
http.get("http://localhost:8001/hello");
sleep(1);
}
运行测试
mkdir -p results
k6 run --out json=results/framework1.json k6-tests/framework1.js
k6 run --out json=results/framework2.json k6-tests/framework2.js
JSON 输出可以使用任意语言或绘图工具进行处理。
分析结果
下面是一个最小的 Python 脚本,用于提取平均延迟、95 百分位延迟、请求计数和失败率,然后绘制平均响应时间。
# analyze.py
import json
import numpy as np
import matplotlib.pyplot as plt
files = {
"Framework1": "results/framework1.json",
"Framework2": "results/framework2.json"
}
summary = {}
for name, file in files.items():
durations, fails, total = [], 0, 0
with open(file) as f:
for line in f:
obj = json.loads(line)
if obj.get("type") == "Point":
metric = obj.get("metric")
if metric == "http_req_duration":
durations.append(obj["data"]["value"])
if metric == "http_req_failed":
fails += obj["data"]["value"]
total += 1
if durations:
summary[name] = {
"avg": np.mean(durations),
"p95": np.percentile(durations, 95),
"requests": len(durations),
"fail_rate": fails / total if total else 0,
}
print(summary)
plt.bar(summary.keys(), [summary[n]["avg"] for n in summary])
plt.title("Average Response Time (ms)")
plt.ylabel("Milliseconds")
plt.show()
关键要点
- Docker isolation 使基准测试可复现。
- Worker count & CPU limits 必须在容器之间保持一致。
- 简单路由可能让 Flask 看起来更快;不要被表象迷惑。
- 异步/I/O 密集型路由能够展示 FastAPI(或其他异步框架)的优势。
- 始终针对实际工作负载进行基准测试,而不仅仅是小示例。
仓库
完整示例(包括 Dockerfiles、k6 脚本和分析代码)可在以下地址获取:
https://github.com/mahdi-shamlou/web_framework_benchmarks