2023年是时候更新你的技术武器库了AsgivsWsgi(F
也许这一篇的标题有那么一点不厚道,因为Asgi(AsynchronousServerGatewayInterface)毕竟是Wsgi(WebServerGatewayInterface)的扩展,而FastAPI毕竟也是站在Flask的肩膀上才有了突飞猛进的发展,大多数人听说Asgi也许是因为Django的最新版(3。0)早已宣布支持Asgi网络规范,这显然是一个振奋人心的消息,2023年,如果你在Web开发面试中不扯一点Asgi,显然就有点落后于形势了。
那么到底啥是Wsgi,什么又是Asgi,放心,不扯CGI,不扯各种抽象概念,简单粗暴理解:
Wsgi是同步通信服务规范,客户端请求一项服务,并等待服务完成,只有当它收到服务的结果时,它才会继续工作。当然了,可以定义一个超时时间,如果服务在规定的时间内没有完成,则认为调用失败,调用方继续工作。
Wsgi简单工作原理示意图:
简单实现:WSGIexampledefapplication(environ,startresponse):startresponse(200OK,〔(ContentType,textplain)〕)returnbHello,Wsgi
Asgi是异步通信服务规范。客户端发起服务呼叫,但不等待结果。调用方立即继续其工作,并不关心结果。如果调用方对结果感兴趣,有一些机制可以让其随时被回调方法返回结果。
Asgi简单工作原理示意图:
简单实现:Asgiexampleasyncdefapplication(scope,receive,send):eventawaitreceive()。。。awaitsend({type:websocket。send,。。。})
简单总结一下:Asgi是异步的,Wsgi是同步的,而基于Wsgi的Flask是同步框架,基于Asgi的FastAPI是异步框架,就这么简单,那么同步框架和异步框架的区别到底在哪儿?为什么要把Flask换成FastAPI?
不靠拍脑门儿、也不是道听途说、人云亦云。玩技术的应该用数据说话,论点永远依托论据,所以我们来简单对两款框架的性能做一个测试,首先分别安装依赖的库。
Flask:pipinstallgunicornpipinstallgeventpipinstallflask
FastAPI:pipinstallfastapipipinstalluvicorn
我们首先干的一件事就是,看看Flask和FastAPI如何处理来自多个客户端的多个请求。特别是当代码存在效率问题时(比如数据库查询时间长这种耗时任务),这里故意使用time。sleep()来模拟耗时任务,为什么不用asyncio呢?因为众所周知的原因:time。sleep是阻塞的。
Flask:fromflaskimportFlaskfromflaskrestfulimportResource,ApifromtimeimportsleepappFlask(name)apiApi(app)classRoot(Resource):defget(self):print(睡10秒)sleep(10)print(醒了)return{message:hello}api。addresource(Root,)ifnamemain:app。run()
FastApi:importuvicornfromfastapiimportFastAPIfromtimeimportsleepappFastAPI()app。get()asyncdefroot():print(睡10秒)sleep(10)print(醒了)return{message:hello}ifnamemain:uvicorn。run(app,host127。0。0。1,port8000)
分别启动服务
Flask:python3manage。py
FastAPI:uvicornmanage:appreload
同时一时间内,开启多个浏览器,分别并发请求首页。
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(请原谅我的机器比较渣)。abn5000c100http:127。0。0。1:5000abn5000c100http:127。0。0。1:8000
这里为了公平起见,Flask配合Gunicorn服务器,开3个worker,FastAPI配合Uvicorn服务器,同样开3个worker。
Flask压测结果:liuyue:mytornadoliuyueabn5000c100http:127。0。0。1:5000ThisisApacheBench,Version2。3Revision:1826891gt;Copyright1996AdamTwiss,ZeusTechnologyLtd,http:www。zeustech。netLicensedtoTheApacheSoftwareFoundation,http:www。apache。orgBenchmarking127。0。0。1(bepatient)Completed500requestsCompleted1000requestsCompleted1500requestsCompleted2000requestsCompleted2500requestsCompleted3000requestsCompleted3500requestsCompleted4000requestsCompleted4500requestsCompleted5000requestsFinished5000requestsServerSoftware:gunicorn20。0。4ServerHostname:127。0。0。1ServerPort:5000DocumentPath:DocumentLength:28bytesConcurrencyLevel:100Timetakenfortests:4。681secondsCompleterequests:5000Failedrequests:0Totaltransferred:1060000bytesHTMLtransferred:140000bytesRequestspersecond:1068。04〔sec〕(mean)Timeperrequest:93。629〔ms〕(mean)Timeperrequest:0。936〔ms〕(mean,acrossallconcurrentrequests)Transferrate:221。12〔Kbytessec〕received
FastAPI压测结果:liuyue:mytornadoliuyueabn5000c100http:127。0。0。1:8000ThisisApacheBench,Version2。3Revision:1826891gt;Copyright1996AdamTwiss,ZeusTechnologyLtd,http:www。zeustech。netLicensedtoTheApacheSoftwareFoundation,http:www。apache。orgBenchmarking127。0。0。1(bepatient)Completed500requestsCompleted1000requestsCompleted1500requestsCompleted2000requestsCompleted2500requestsCompleted3000requestsCompleted3500requestsCompleted4000requestsCompleted4500requestsCompleted5000requestsFinished5000requestsServerSoftware:uvicornServerHostname:127。0。0。1ServerPort:8000DocumentPath:DocumentLength:19bytesConcurrencyLevel:100Timetakenfortests:2。060secondsCompleterequests:5000Failedrequests:0Totaltransferred:720000bytesHTMLtransferred:95000bytesRequestspersecond:2426。78〔sec〕(mean)Timeperrequest:41。207〔ms〕(mean)Timeperrequest:0。412〔ms〕(mean,acrossallconcurrentrequests)Transferrate:341。27〔Kbytessec〕received
显而易见,5000个总请求,Flask花费4。681秒,每秒可以处理1068。04个请求,而FastAPI花费2。060秒,每秒可以处理2426。78个请求。
结语:曾几何时,当人们谈论Python框架的性能时,总是不自觉的嗤之以鼻,而现在,Python异步生态正在发生着惊天动地的变化,新的框架应运而生(Sanic、FastAPI),旧的框架正在重构(Django3。0),很多库也开始支持异步(httpx、Sqlalchemy、Mortor)。软件科技发展的历史表明,一项新技术的出现和应用,常常会给这个领域带来深刻的变革,古语有云:察势者智,顺势者赢,驭势者独步天下。所以,只有拥抱未来、拥抱新技术、顺应时代才是正确的、可持续发展的道路。