python 异步 web 框架性能对比:tornado、tornado-uvloop、fastap、sanic

目录

先上结论

实验代码

安装包

服务端代码

tornado异步

tornado_uvloop异步 

fastapi异步

sanic异步

异步请求代码

ab测试代码

ab命令详解

ab模拟GET请求

ab模拟POST请求


 

先上结论

sanic处理get请求最快,是其他框架的2-4倍

sanic处理post请求最快,是其他框架的2-2.5倍

tornado替换uvloop,能带来1/10的提升,但提升很有限

实际生产部署(平均4000行代码,2次Redis操作,3次post请求),对比tornado和sanic,都是17rps,两个框架没有性能差别,目前分析发现:

1.aioredis效率不稳定,单独测试1000并发时,平均耗时增加1s/每次,不知是否与Redis的单机部署有关,但这个不是最后瓶颈

2.一个业务接口,总逻辑代码近8000行,无io操作,Python实际运行行数大概在2000-6000行,耗时25-50ms不等,效率很低,即使异步无io操作,已经决定了1s只能处理最多50个请求

3.协程之前切换切换效率低:测试代码中仅仅加一个sleep 0.1s,tornado并发由1555降低到400,再次说明由于Python本身运行效率,导致切换速度并不快

综上,对于高并发、复杂逻辑的服务,Python无论本身运行效率还是协程切换效率都是瓶颈,还是直接用go开发吧

 

 tornadotornado_uvloopsanicfastapi
get并发RPS1002169141452025
get平均耗时ms99592449
post并发RPS1555173141182008
post平均耗时ms64572449

   

  

 

实验代码

安装包

pip install sanic==20.6.2

pip install fastapi==0.61.0

pip install uvicorn

pip install tornado==6.0.2

 

服务端代码

tornado异步

#run_tornado.py

# tornado-6.0.2


import tornado.ioloop
import tornado.web
import json
class MainHandler(tornado.web.RequestHandler):
    async def get(self):
        self.write("Hello, world")

    async def post(self):
        self.write(json.dumps({"code": "0000", "msg": "Hello, world"}))

def make_app():
    return tornado.web.Application([
        (r"/test", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8001)
    tornado.ioloop.IOLoop.current().start()

 

tornado_uvloop异步 

# run_tornado_uvloop.py

# tornado-6.0.2
import asyncio

import tornado.ioloop
import tornado.web
import json

import uvloop
from tornado.platform.asyncio import AsyncIOMainLoop


class MainHandler(tornado.web.RequestHandler):
    async def get(self):
        self.write("Hello, world")

    async def post(self):
        self.write(json.dumps({"code": "0000", "msg": "Hello, world"}))

def make_app():
    return tornado.web.Application([
        (r"/test", MainHandler),
    ])


if __name__ == '__main__':
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    AsyncIOMainLoop().install()
    app = make_app()
    app.listen(8003)
    asyncio.get_event_loop().run_forever()

 

 

fastapi异步

 

# run_fastapi.py
from typing import Optional

from fastapi import FastAPI

app = FastAPI()


@app.get("/test")
def read_root():
    return "Hello, world"


@app.post("/test")
def read_item():
    return {"code": "0000", "msg": "Hello, world"}

fast_api启动命令:nohup uvicorn run_fastapi:app --reload &

 

sanic异步

# run_sanic.py

from sanic import Sanic
from sanic.response import json,html

app = Sanic()

@app.route('/test',methods=['GET'])
async def hello(request):
    return html("Hello, world")

@app.route('/test', methods=['POST'])
async def hello2(request):
    return json({"code": "0000", "msg": "Hello, world"})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8002)

sanic启动命令:nohup python run_sanic.py  &

 

异步请求代码

# async_request.py

import time
import httpx
import asyncio

loop = asyncio.get_event_loop()

async def requ(port):
    url = f"http://localhost:{port}/test"
    s = time.time()
    async with httpx.AsyncClient() as client:
        res = await client.get(url=url)
        # print(port,res.text)
    # print(time.time() - s)

    s = time.time()
    async with httpx.AsyncClient() as client:
        res = await client.post(url=url)
        # print(port,res.text)
    # print(time.time() - s)

for port in [8000,8001,8002,8003]:
    tasks = asyncio.gather(*[requ(port)  for i in range(100)])
    ss = time.time()
    loop.run_until_complete(tasks)
    print(f"{port}总耗时:",time.time() - ss)

# 8000总耗时: 3.102787733078003
# 8001总耗时: 3.017672061920166
# 8002总耗时: 3.00693678855896
# 8003总耗时: 3.008575916290283


ab测试代码

测试get请求

ab -n 100 -c 100  http://localhost:8000/test

测试post请求

ab -n 100 -c 100 -T 'application/json'   http://localhost:8000/test

 

ab命令详解

ab 命令参数说明:

-n   发送请求的数量

-c   同一时刻模拟用户的数量

-s   每个响应的最长等待时间  默认是30秒

-k    

-p   发送post请求参数存放的文件  (使用此选项的时候一定要加上-T参数)

-T   文本类型   默认为’text-plain‘  post请求时一般为'application/x-www-form-urlencoded'

 

ab模拟GET请求

ab -n 100 -c 10 'http://testurl.com/xxxx?para1=aaa&para2=bbb'

ab模拟POST请求

需要把将要post的数据(一般是json格式)放在文件里。比如一个post接口需要如下方式访问

curl -H 'Content-Type:application/json' -X POST  -d '{"actionType":"collect","appId":1,"contentId":"1770730744","contentType":"musictrack","did":"866479025346031","endType":"mobile","recommendId":"104169490_1_0_1434453099#1770730744#musictrack#USER_TO_SONG_TO_SONGS#gsql_similarity_content2content","tabId":0,"uid":"104169490"}' http://localhost:8083/query/leui/v0/post/user/behavior/content

需要吧-d 后面的json数据放在一个文件里,比如建立一个文件post_data.txt,放入:

{"actionType":"collect","appId":1,"contentId":"1770730744","contentType":"musictrack","did":"866479025346031","endType":"mobile","recommendId":"104169490_1_0_1434453099#1770730744#musictrack#USER_TO_SONG_TO_SONGS#gsql_similarity_content2content","tabId":0,"uid":"104169490"}

然后用-p参数解析并发送这个json数据:

ab -n 100 -c 10 -p post_data.txt -T 'application/json' http://localhost:8083/query/leui/v0/post/user/behavior/content

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值