FastAPI性能碾压Flask?

不止一次的听过,FastAPI性能碾压Flask,直追Golang,不过一直没有测试过,今天闲着没事测试一下看看结果。不知道是哪里出了问题,结果大跌眼镜

测试之前

为了偷懒,自然想先从网上找找前人的测试代码以作为参照。百度前几名关于FastAPI和Flask性能测试又带了代码的有下面几个:

有点疑惑

简单看了一下,没明白,为什么都用了uvicorn启动FastAPI,却只用Flask自带的启动方式,为什么不用其他WSGI服务器?

我觉得这样应该是有问题的,且不说本来二者都不是同一层次的框架(FastAPI是基于Starlette的,这才是应该和Flask对比的框架),就算对比,也应该用差不多的启动方式吧?

uvicorn是个第三方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 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线程,uvicorn默认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

把uvicorn修改为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)
  • uvicorn用4线程启动提升很小(842:583/631),还没有waitress单线程快

这个结果很出乎意料,我现在有点不自信了,是我测试过程哪里不对吗?

理论上说不通啊,uvicorn开4线程后结果只有1倍多,waitress开4线程快了2倍多,代表着4线程都没完全利用到,而且uvicorn单线程足处理能力更强吧,不知道为什么结果差这么多。

可能是测试工具的原因吧,毕竟别人都用的AB,还都指定并发数,我用的siege,没限制并发。

而且uvicorn文档还提到可以使用Gunicorn管理进程,可能性能还会提升,碍于设备原因我就不测试了。

换工具重新测试

实在信不过这个结果,第二天又换了hey压测工具测试一次,20并发10秒

.\hey_windows_amd64.exe -c 20 -z 10s http://127.0.0.1:5000/

结果如下:

  • flask

    Summary:
    Total:        10.0802 secs
    Slowest:      0.1169 secs
    Fastest:      0.0056 secs
    Average:      0.0389 secs
    Requests/sec: 512.5894
  • flask + waitress 1线程

    Summary:
    Total:        10.0128 secs
    Slowest:      0.0721 secs
    Fastest:      0.0013 secs
    Average:      0.0199 secs
    Requests/sec: 1006.4087
  • fastapi + uvicorn 1线程

    Summary:
    Total:        10.0161 secs
    Slowest:      0.0888 secs
    Fastest:      0.0031 secs
    Average:      0.0225 secs
    Requests/sec: 886.4733
  • fastapi + uvicorn + async 1线程

    Summary:
    Total:        10.0218 secs
    Slowest:      0.0808 secs
    Fastest:      0.0057 secs
    Average:      0.0210 secs
    Requests/sec: 951.3288
  • flask + waitress 4线程

    Summary:
    Total:        10.0051 secs
    Slowest:      0.0912 secs
    Fastest:      0.0016 secs
    Average:      0.0134 secs
    Requests/sec: 1486.4384
  • fastapi + uvicorn 4线程

    Summary:
    Total:        10.0386 secs
    Slowest:      0.0925 secs
    Fastest:      0.0018 secs
    Average:      0.0154 secs
    Requests/sec: 1292.3074
  • fastapi + uvicorn + async 4线程

    Summary:
    Total:        10.0108 secs
    Slowest:      0.0853 secs
    Fastest:      0.0016 secs
    Average:      0.0134 secs
    Requests/sec: 1489.9933

    从结果QPS和平均响应时间可以看出:

  • Flask直接启动结果比FastAPI启动结果差了接近一半
  • FastAPI用异步async还是有一些提升的
  • Flask用waitress WSGI服务器启动与FastAPI用uvicorn+异步结果基本相当
  • Flask用waitress WSGI服务器单线程启动结果比之前快了近1倍,也比FastAPI快一点

平均响应时间(秒)
QPS

写在最后

做这个测试的本意是反驳前文提到的原因,只是想说,对比测试时应该使用第三方WSGI服务器启动Flask。

最终测试结果我也有点迷糊了,只能保证测试数据和代码绝对真实,看到本文的朋友最好自己测试一遍。

另外,性能测试肯定要加上基本功能,起码要有数据接收、处理、返回整个流程吧,只测试HelloWorld没什么代表性。

不否认FastAPI的优点,比如支持异步、ws、自动生成文档、强调声明变量类型等,但Flask其实也挺好。

连写文带测试花了几个小时,闲的。

你可能感兴趣的:(FastAPI性能碾压Flask?)