如何以公平、隔离的方式对 Web 框架进行基准测试 | Mahdi Shamlou

发布: (2026年2月22日 GMT+8 04:39)
5 分钟阅读
原文: Dev.to

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

您可以将 framework1framework2 替换为您想比较的任何框架。演示中我们使用一个简单的 /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
0 浏览
Back to Blog

相关文章

阅读更多 »