fastapi 接口阻塞问题处理

fastapi 接口阻塞问题处理

背景

在使用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
改为如下:

@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 将程序挂起。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值