乐趣区

关于python:2020年是时候更新你的技术武器库了Asgi-vs-WsgiFastAPI-vs-Flask

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_167

兴许这一篇的题目有那么一点不厚道,因为 Asgi(Asynchronous Server Gateway Interface)毕竟是 Wsgi(Web Server Gateway Interface)的扩大,而 FastAPI 毕竟也是站在 Flask 的肩膀上才有了突飞猛进的倒退,大多数人据说 Asgi 兴许是因为 Django 的最新版 (3.0) 早已发表反对 Asgi 网络标准,这显然是一个振奋人心的音讯,2020 年,如果你在 Web 开发面试中不扯一点 Asgi,显然就有点落后于局势了。

那么到底啥是 Wsgi,什么又是 Asgi,释怀,不扯 CGI,不扯各种抽象概念,简略粗犷了解:

Wsgi 是同步通信服务标准,客户端申请一项服务,并期待服务实现,只有当它收到服务的后果时,它才会持续工作。当然了,能够定义一个超时工夫,如果服务在规定的工夫内没有实现,则认为调用失败,调用方持续工作。

Wsgi 简略工作原理示意图:

简略实现:

#WSGI example   
  
  
def application(environ, start_response):  
  
  
    start_response('200 OK', [('Content-Type', 'text/plain')])  
  
  
    return b'Hello, Wsgi\n'

Asgi 是异步通信服务标准。客户端发动服务呼叫,但不期待后果。调用方立刻持续其工作,并不关怀后果。如果调用方对后果感兴趣,有一些机制能够让其随时被回调办法返回后果。

Asgi 简略工作原理示意图:

简略实现:

#Asgi example  
  
async def application(scope, receive, send):  
  
  
    event = await receive()  
  
  
    ...  
  
  
    await send({"type": "websocket.send", ...})

简略总结一下:Asgi 是异步的,Wsgi 是同步的,而基于 Wsgi 的 Flask 是同步框架,基于 Asgi 的 FastAPI 是异步框架,就这么简略,那么同步框架和异步框架的区别到底在哪儿?为什么要把 Flask 换成 FastAPI?

不靠拍脑门儿、也不是一人传虚; 万人传实、随声附和。玩技术的应该用数据谈话,论点永远依靠论据,所以咱们来简略对两款框架的性能做一个测试,首先别离装置依赖的库。

Flask:

pip install gunicorn  
pip install gevent  
pip install flask

FastAPI:

pip install fastapi  
pip install uvicorn

咱们首先干的一件事就是,看看 Flask 和 FastAPI 如何解决来自多个客户端的多个申请。特地是当代码存在效率问题时(比方数据库查问工夫长这种耗时工作),这里成心应用 time.sleep()来模仿耗时工作,为什么不必 asyncio 呢?因为家喻户晓的起因:time.sleep 是阻塞的。

Flask:

from flask import Flask  
from flask_restful import Resource, Api  
from time import sleep  
  
app = Flask(__name__)  
api = Api(app)  
  
class Root(Resource):  
    def get(self):  
        print('睡 10 秒')  
        sleep(10)  
        print('醒了')  
        return {'message': 'hello'}  
  
api.add_resource(Root, '/')  
  
if __name__ == "__main__":  
    app.run()

FastApi:

import uvicorn  
from fastapi import FastAPI  
from time import sleep  
app = FastAPI()  
  
@app.get('/')  
async def root():  
    print('睡 10 秒')  
    sleep(10)  
    print('醒了')  
    return {'message': 'hello'}  
  
if __name__ == "__main__":  
    uvicorn.run(app, host="127.0.0.1", port=8000)

别离启动服务

Flask:python3 manage.py

FastAPI:uvicorn manage:app –reload

同时一时间内,开启多个浏览器,别离并发申请首页。

Flask:http://localhost:5000

FastAPI:http://localhost:8000

察看后盾打印后果:

Flask:

FastAPI:

能够看到,同样的四次申请,Flask 先是阻塞了 40 秒,而后顺次返回后果,FastAPI 则是第一次阻塞后间接返回,这代表了在 FastAPI 中阻塞了一个事件队列,证实 FastAPI 是异步框架,而在 Flask 中,申请可能是在新线程中运行的。将所有 CPU 绑定的工作移到独自的过程中,所以在 FastAPI 的例子中,只是在事件循环中 sleep(所以异步框架这里最好不要应用 time.sleep 而是 asyncio.sleep)。在 FastAPI 中,异步运行 IO 绑定的工作。

当然这不能阐明太多问题,咱们持续应用鼎鼎有名的 ApacheBench 别离对两款框架进行压测。

一共设置 5000 个申请,QPS 是 100(请原谅我的机器比拟渣)。

ab -n 5000 -c 100 http://127.0.0.1:5000/  
ab -n 5000 -c 100 http://127.0.0.1:8000/

这里为了偏心起见,Flask 配合 Gunicorn 服务器,开 3 个 worker,FastAPI 配合 Uvicorn 服务器,同样开 3 个 worker。

Flask 压测后果:

liuyue:mytornado liuyue$ ab -n 5000 -c 100 http://127.0.0.1:5000/  
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking 127.0.0.1 (be patient)  
Completed 500 requests  
Completed 1000 requests  
Completed 1500 requests  
Completed 2000 requests  
Completed 2500 requests  
Completed 3000 requests  
Completed 3500 requests  
Completed 4000 requests  
Completed 4500 requests  
Completed 5000 requests  
Finished 5000 requests  
  
  
Server Software:        gunicorn/20.0.4  
Server Hostname:        127.0.0.1  
Server Port:            5000  
  
Document Path:          /  
Document Length:        28 bytes  
  
Concurrency Level:      100  
Time taken for tests:   4.681 seconds  
Complete requests:      5000  
Failed requests:        0  
Total transferred:      1060000 bytes  
HTML transferred:       140000 bytes  
Requests per second:    1068.04 [#/sec] (mean)  
Time per request:       93.629 [ms] (mean)  
Time per request:       0.936 [ms] (mean, across all concurrent requests)  
Transfer rate:          221.12 [Kbytes/sec] received

FastAPI 压测后果:

liuyue:mytornado liuyue$ ab -n 5000 -c 100 http://127.0.0.1:8000/  
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>  
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/  
Licensed to The Apache Software Foundation, http://www.apache.org/  
  
Benchmarking 127.0.0.1 (be patient)  
Completed 500 requests  
Completed 1000 requests  
Completed 1500 requests  
Completed 2000 requests  
Completed 2500 requests  
Completed 3000 requests  
Completed 3500 requests  
Completed 4000 requests  
Completed 4500 requests  
Completed 5000 requests  
Finished 5000 requests  
  
  
Server Software:        uvicorn  
Server Hostname:        127.0.0.1  
Server Port:            8000  
  
Document Path:          /  
Document Length:        19 bytes  
  
Concurrency Level:      100  
Time taken for tests:   2.060 seconds  
Complete requests:      5000  
Failed requests:        0  
Total transferred:      720000 bytes  
HTML transferred:       95000 bytes  
Requests per second:    2426.78 [#/sec] (mean)  
Time per request:       41.207 [ms] (mean)  
Time per request:       0.412 [ms] (mean, across all concurrent requests)  
Transfer rate:          341.27 [Kbytes/sec] received

不言而喻,5000 个总申请,Flask 破费 4.681 秒,每秒能够解决 1068.04 个申请,而 FastAPI 破费 2.060 秒,每秒能够解决 2426.78 个申请。

结语:曾几何时,当人们议论 Python 框架的性能时,总是不盲目的不屑一顾,而当初,Python 异步生态正在产生着惊天动地的变动,新的框架应运而生(Sanic、FastAPI),旧的框架正在重构(Django3.0),很多库也开始反对异步(httpx、Sqlalchemy、Mortor)。软件科技倒退的历史表明,一项新技术的呈现和利用,经常会给这个畛域带来粗浅的改革,古语有云:察势者智,趁势者赢,驭势者独步天下。所以,只有拥抱将来、拥抱新技术、顺应时代才是正确的、可继续倒退的路线。

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_167

退出移动版