共计 4004 个字符,预计需要花费 11 分钟才能阅读完成。
不止一次的听过,有个 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 框架。
开始测试
网上都是用 AB 测试的,我电脑没装 Apache,就用了另一个测试工具 siege。测试形式为不限连接数,测试 10 秒,命令如下:
./siege.exe -b -t10s http://127.0.0.1:5000/
测试代码和之前搜到的一样,用二者官网的例子,输入 HelloWorld,略作批改,把启动代码写进文件内,就不必应用命令行启动了。
-
Flask
from flask import Flask from waitress import serve app = 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 uvicorn app = 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。
当初最终测试后果我也不敢确定了,只能保障测试数据和代码相对实在,看到本文的敌人最好本人测试一遍,顺便通知我为什么会呈现这个后果。
还是多说一句,网上太多无脑吹 FastAPI 的人了,不否定它的长处,比方反对异步、ws、主动生成文档、强调申明变量类型等,但也没必要死踩 Flask 上位。
连写文带测试花了几个小时,闲的。