fastapi 里面的同步异步问题

资料

https://zhuanlan.zhihu.com/p/720868393

实践 

将数据库查询操作的函数全部改为异步的即可

本地起一个简单的fastapi服务,定义4个接口,代码如下:

from fastapi import FastAPI
import time
import asyncio


router = FastAPI()


@router.get("/a")
async def a():
    time.sleep(1)
    return {"message": "异步模式,但是同步执行sleep函数,执行过程是串行的"}


@router.get("/b")
async def b():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, time.sleep, 1)
    return {"message": "线程池中运行sleep函数"}


@router.get("/c")
async def c():
    await asyncio.sleep(1)
    return {"message": "异步模式,且异步执行sleep函数"}


@router.get("/d")
def d():
    time.sleep(1)
    return {"message": "同步模式,但是FastAPI会放在线程池中运行,所以很快"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:router", host="0.0.0.0", port=5001, reload=True)

本地运行以上服务,端口5001。

接下来使用grequests库,并发100个请求分别测试这4个接口。并发测试的脚本如下:

import time

import grequests
from superstream import Stream

url = "http://127.0.0.1:5001/a"  # 其他接口修改此处路径即可
urls = [url for _ in range(100)]

req_list = (grequests.get(url) for url in urls)  # 构建请求列表,注意grequests.get(url)返回的是一个请求对象,并没有真正发起请求。
star_time = time.time()  # 请求开始前掐表计时
rsp_list = grequests.map(req_list)  # 发送请求,等响应全部完成后,返回一个响应列表
end_time = time.time()  # 请求结束后掐表计时
print(f"100并发的耗时:{end_time - star_time}")

# Stream(rsp_list).for_each(lambda rsp: print(rsp.text))

测试结果如下:

  • /a:100并发的耗时:100.49345779418945
  • /b:100并发的耗时:8.086071014404297
  • /c:100并发的耗时:1.136078119277954
  • /d:100并发的耗时:3.126424789428711

从上面实验的结果看,如果使用了async函数,函数体中无await,则每个请求都会阻塞,执行过程实际是串行的;如果函数体中有await,则请求不会阻塞,执行过程则是并行的。如果不使用async函数,FastAPI会将函数放到thread pool中执行,也就是说默认就是多线程来处理请求。

总结一下,首先按照官方说明,无论你是否使用async,FastAPI都会采用异步的方式处理。但是,如果你定义了async函数,函数体却是同步的调用(例:/a接口),将导致函数执行过程变成串行。所以如果不清楚是否需要使用async函数,那么直接使用普通函数即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值