无脑吹FastAPI性能碾压Flask(关于网上不合适的性能对比以及让我糊涂的自测结果)

不止一次的听过,有个FastAPI框架,性能碾压Flask,直追Golang,不过一直没有测试过,今天闲着没事测试一下看看结果。不知道是哪里出了问题,结果大跌眼镜。
测试之前 为了偷懒,自然想先从网上找找前人的测试代码以作为参照。百度前几名关于FastAPI和Flask性能测试又带了代码的有下面几个:

  • FastAPI、Flask、Golang性能测试
  • Flask、Django、Tornado、FastAPI 之 Python Web 并发测试
  • flask,tornado,fastapi 压测比较(web框架)
有点疑惑 简单看了一下,没明白,为什么都用了unicorn启动FastAPI,却只用Flask自带的启动方式,为什么不用其他WSGI服务器?
我觉得这样应该是有问题的,且不说本来二者都不是同一层次的框架(FastAPI是基于Starlette的,这才是应该和Flask对比的框架),就算对比,也应该用差不多的启动方式吧?
unicorn是个第三方ASGI服务器,Flask应该用一个第三方WSGI服务器来启动才正常吧?感觉用它自带的WSGI服务器比可能不太公平。
我本来想用gunicorn来启动Flask进行对比的,结果发现不兼容Windows,所以换了个waitress,差不多的WSGI框架。
开始测试
  • 环境:
    Win10 Python3.8.9 各依赖库全是最新版
网上都是用AB测试的,我电脑没装Apache,就用了另一个测试工具siege。测试方式为不限连接数,测试10秒,命令如下:
./siege.exe -b -t10s http://127.0.0.1:5000/

测试代码和之前搜到的一样,用二者官网的例子,输出HelloWorld,略作修改,把启动代码写进文件内,就不用使用命令行启动了。
  • Flask
    from flask import Flask from waitress import serveapp = Flask(__name__)@app.route('/') def index(): return {'message': 'hello world'}if __name__ == '__main__': app.run(host='0.0.0.0') # serve(app, host='0.0.0.0', port=5000)

  • FastAPI
    from fastapi import FastAPI import uvicornapp = FastAPI()@app.get("/") async def read_root(): return {"Hello": "World"}if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=5000)

测试结果 鉴于网上的文章在那摆着,所以我也测试了一下使用Flask自带启动方式的结果。
除此之外,还测试了FastAPI使用异步的结果(就加了个async,实际应该什么没用的,文档中明确说了,只有函数内部使用了异步函数且需要同步返回时,也就是需要在内部用await时,才需要定义async)。
结果如下:
  • flask
    Transactions:4579 hits Availability:100.00 % Elapsed time:9.15 secs Data transferred:0.11 MB Response time:0.03 secs Transaction rate:500.66 trans/sec Throughput:0.01 MB/sec Concurrency:14.93 Successful transactions:4579 Failed transactions:0 Longest transaction:0.10 Shortest transaction:0.02

  • flask + waitress
    Transactions:12598 hits Availability:100.00 % Elapsed time:10.02 secs Data transferred:0.31 MB Response time:0.01 secs Transaction rate:1257.03 trans/sec Throughput:0.03 MB/sec Concurrency:14.89 Successful transactions:12598 Failed transactions:0 Longest transaction:0.03 Shortest transaction:0.00

  • fastapi + uvicorn
    Transactions:5278 hits Availability:100.00 % \Elapsed time:9.05 secs Data transferred:0.09 MB Response time:0.03 secs Transaction rate:583.20 trans/sec Throughput:0.01 MB/sec Concurrency:14.93 Successful transactions:5278 Failed transactions:0 Longest transaction:0.11 Shortest transaction:0.01

  • fastapi + uvicorn + async
    Transactions:5876 hits Availability:100.00 % \Elapsed time:9.31 secs Data transferred:0.10 MB Response time:0.02 secs Transaction rate:631.22 trans/sec Throughput:0.01 MB/sec Concurrency:14.84 Successful transactions:5876 Failed transactions:0 Longest transaction:0.12 Shortest transaction:0.00

从Transaction rate也就是请求处理速率可以看到:
  • Flask直接启动结果比FastAPI启动结果略差一些(500:583/631)
  • FastAPI用不用异步async差别不大(583:631)
  • Flask用waitress WSGI服务器启动结果比不用快了2.5倍(1257:500),同样也比FastAPI快2倍左右
这个结果和其他人测试的完全不同,与我预估的也有差距,感觉是哪里出错了?
Flask直接启动比FastAPI慢一点是在意料之中的,但是使用waitress WSGI服务器启动后快这么多肯定也是不正常的。
于是我去查看了二者启动的源码,发现waitress默认4线程,unicorn默认1线程。。。
只好把Flask修改为1线程重新测试
serve(app, host='0.0.0.0', port=5000, threads=1)

结果如下:
Transactions:7492 hits Availability:100.00 % Elapsed time:9.07 secs Data transferred:0.19 MB Response time:0.02 secs Transaction rate:825.84 trans/sec Throughput:0.02 MB/sec Concurrency:14.89 Successful transactions:7492 Failed transactions:0 Longest transaction:0.07 Shortest transaction:0.01

把unicorn修改为4线程重新测试
uvicorn.run("test-fastapi:app", host="0.0.0.0", port=5000, workers=4)# 需要同目录下新建`pyproject.toml`文件,内容为: [tool.poetry.scripts] start = "test-fastapi:start"

结果如下:
Transactions:7782 hits Availability:100.00 % Elapsed time:9.24 secs Data transferred:0.13 MB Response time:0.02 secs Transaction rate:842.39 trans/sec Throughput:0.01 MB/sec Concurrency:14.92 Successful transactions:7782 Failed transactions:0 Longest transaction:0.15 Shortest transaction:0.00

可以看出:
  • Flask用waitress WSGI服务器单线程启动结果比不用快了65%(825:500),同样也比FastAPI快很多(825:583/631)
  • unicorn用4线程启动提升很小(842:583/631),和waitress单线程差不多
这个结果也很出乎意料,我现在有点不自信了,是我测试过程哪里不对吗?
理论上说不通啊,unicorn开4线程后结果只有1倍多,waitress开4线程快了2倍多,代表着4线程都没完全利用到,而且unicorn单线程足处理能力更强吧,不知道为什么结果差这么多。
可能是测试工具的原因吧,毕竟别人都用的AB,还都指定并发数,我用的siege,没限制并发。
而且unicorn文档还提到可以使用Gunicorn管理进程,可能性能还会提升,碍于设备原因我就不测试了。
写在最后 做这个测试的本意是反驳前文提到的原因,只是想说,对比测试时应该使用第三方WSGI服务器启动Flask。
另外,性能测试肯定要加上基本功能啊,起码要有数据接收、处理、返回整个流程吧,只测试HelloWorld没什么代表性。
现在最终测试结果我也不敢确定了,只能保证测试数据和代码绝对真实,看到本文的朋友最好自己测试一遍,顺便告诉我为什么会出现这个结果。
还是多说一句,网上太多无脑吹FastAPI的人了,不否认它的优点,比如支持异步、ws、自动生成文档、强调声明变量类型等,但也没必要死踩Flask上位。
【无脑吹FastAPI性能碾压Flask(关于网上不合适的性能对比以及让我糊涂的自测结果)】连写文带测试花了几个小时,闲的。

    推荐阅读