fastapi 接口阻塞问题处理
原文地址:https://blog.csdn.net/qq_42006301/article/details/124873383
背景
在使用fastapi框架开发时,遇到一个坑,在请求某个耗时的接口时,再请求其他接口,发现请求都被阻塞卡住了,经过排查问题,发现是async使用不当导致的
问题复现
这里写了一个小demo文件,里面有耗时函数work 和 两个接口 /test1、 /test2
def work(s):
print("work start work")
time.sleep(s)
print("work end work")
@api.get("/test1")
async def test1():
print("func: test1")
return "test1"
@api.get("/test2")
async def test2():
a = int(time.time())
print(f"start: {a}")
work(8)
b = int(time.time())
print(f"start: {b-a}")
return f"test2耗时:{b-a}"
使用如下测试脚本请求接口测试
import time
import requests
from threading import Thread
def task1():
a = int(time.time())
requests.get(url="http://127.0.0.1:8000/test/test1")
b = int(time.time())
print(f"test1耗时{b - a}")
def task2():
a = int(time.time())
requests.get(url="http://127.0.0.1:8000/test/test2")
b = int(time.time())
print(f"test2耗时{b - a}")
Thread(target=task1).start()
Thread(target=task2).start()
运行测试脚本发现,/test1被阻塞了,等/test2处理完之后,/test1 才返回结果
执行顺序如下
test2--start: 1652973708
work start work
work end work
test2--end: 1652973716
test1--执行
INFO: 127.0.0.1:63965 - "GET /test/test2 HTTP/1.1" 200 OK
INFO: 127.0.0.1:63966 - "GET /test/test1 HTTP/1.1" 200 OK
测试脚本
test1耗时8
test2耗时8
Process finished with exit code 0
可以看到,/test1在/test2执行完之后才执行,/test1请求同样耗时8秒
解决方法1
直接删除掉 /test2 的 async(fastapi会按照多线程的方式进行处理)
改为如下:
@api.get("/test2")
def test2():
a = int(time.time())
print(f"test2--start: {a}")
work(8)
b = int(time.time())
print(f"test2--end: {b}")
return f"test2耗时:{b-a}"
此时运行测试脚本,执行顺序如下
test1--执行
test2--start: 1652973918
work start work
INFO: 127.0.0.1:64024 - "GET /test/test1 HTTP/1.1" 200 OK
work end work
test2--end: 1652973926
INFO: 127.0.0.1:64023 - "GET /test/test2 HTTP/1.1" 200 OK
耗时
test1耗时0
test2耗时8
Process finished with exit code 0
解决方法2
利用asyncio获取当前的event loop。
然后将耗时函数work放到一个event loop中去运行
@api.get("/test2")
async def test2():
a = int(time.time())
print(f"test2--start: {a}")
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, work, 8)
b = int(time.time())
print(f"test2--end: {b}")
return f"test2耗时:{b-a}"
执行顺序如下
test2--start: 1652974163
work start work
test1--执行
INFO: 127.0.0.1:64094 - "GET /test/test1 HTTP/1.1" 200 OK
work end work
test2--end: 1652974171
INFO: 127.0.0.1:64093 - "GET /test/test2 HTTP/1.1" 200 OK
耗时
test1耗时0
test2耗时8
Process finished with exit code 0
总结
如果定义了async函数,函数体却是同步的调用,将导致函数执行过程变成串行,所以在 async函数里如果有io等阻塞的地方一定要使用 await 将程序挂起。