Python 同时启动函数并提前返回

Python 同时启动函数并提前返回,python,python-asyncio,fastapi,Python,Python Asyncio,Fastapi,我需要从FastAPI路径操作返回一个响应,但在此之前,我想发送一个慢速请求,不需要等待该请求的结果,只要记录错误(如果有)。我可以通过Python和FastAPI实现这一点吗?我不想在这个项目中添加芹菜 到目前为止,我已经知道了,但它是同步运行的: 导入异步IO 导入请求 异步def slow_请求(数据): url='1〕https://external.service' response=requests.post( url=url, json=数据, headers={'Auth-Hea

我需要从FastAPI路径操作返回一个响应,但在此之前,我想发送一个慢速请求,不需要等待该请求的结果,只要记录错误(如果有)。我可以通过Python和FastAPI实现这一点吗?我不想在这个项目中添加芹菜

到目前为止,我已经知道了,但它是同步运行的:

导入异步IO
导入请求
异步def slow_请求(数据):
url='1〕https://external.service'
response=requests.post(
url=url,
json=数据,
headers={'Auth-Header':settings.API_TOKEN}
)
如果没有响应,状态\代码==200:
logger.error('response:',response.status_代码)
logger.error('data',data)
@router.post(“/order/”)
异步定义句柄\u顺序(顺序:顺序):
json_数据={
“订单”:订单
}
task=asyncio.create\u任务(
慢速请求(json_数据)
)
等待任务
返回{'body':{'message':'success'}

好的,如果没有人想发布答案,以下是解决方案:

解决方案#1 我们可以按照alex_noname的建议删除
等待任务
行。它将起作用,因为
create_task
计划任务,我们不再等待它的完成

@router.post(“/order/”)
异步定义句柄\u顺序(顺序:顺序):
json_数据={
“订单”:订单
}
task=asyncio.create\u任务(
慢速请求(json_数据)
)
返回{'body':{'message':'success'}
解决方案#2 我最终得到了HTF的建议,因为我已经在使用FastAPI了,这个解决方案对我来说似乎更简洁

@router.post(“/order/”)
异步定义句柄顺序(顺序:顺序,背景任务:背景任务):
json_数据={
“订单”:订单
}
后台任务。添加任务(慢速请求、json数据)
返回{'body':{'message':'success'}

这甚至可以在
def slow\u请求(数据)之前不使用
async
的情况下工作:

问题实际上是两部分的

  • 请求库是同步的,因此
    requests.post(…)
    将阻止事件循环,直到完成
  • 您不需要web请求的结果来响应客户端,但在请求完成之前,您当前的处理程序无法响应客户端(即使是异步的)
考虑将请求逻辑分离到另一个进程中,这样它就可以以自己的速度发生

关键在于,您可以将工作放入某种类型的队列中最终完成,而无需直接将结果用于响应客户端

您可以使用一个异步http请求库和一些回调集合、
multiprocessing
来生成一个新的进程,或者一些更奇特的东西,比如一个独立的程序(可能有一个管道或套接字来通信)

也许这种形式的东西对你有用

导入base64
导入json
导入多处理
URL_外部_服务=”https://example.com"
超时_请求=(2,10)#始终为请求设置超时
SHARED_QUEUE=multiprocessing.QUEUE()#可能会泄漏为无界
异步def slow_请求(数据):
共享队列.put(数据)
#现在,在成功的队列放置时返回,而不是请求完成时返回
def请求_循环(记录器、Q、url、令牌):
虽然为True:#希望是一个守护进程
data=json.dumps(Q.get())#阻塞直到检索(非守护进程可以在此处使用sentinel)
response=requests.post(
网址,
json=数据,
headers={'Auth-Header':标记},
超时=超时\u请求,
)
#为_status()引发_-->try/except-->log+continue
if response.status_代码!=200:
logger.error('调用{}失败(代码={}),数据为:{}'。格式(
url,response.status\u代码,
“base64:”+base64.b64encode(data.encode())
))
def startup():#启动时运行我
#做任何需要做的事情
#如果您可能需要处理大量请求,请创建一个池
p=多处理。进程(
目标=请求_循环,
kwargs={“logger”:logger,“Q”:共享_队列,“url”:url\u外部_服务,“token”:settings.API\u token},
daemon=True
)
p、 开始()

如果您想启动并忘记任务,只需删除
wait task
语句即可
create\u task
已经安排了任务。@alex\u noname它可以工作了,谢谢!我非常接近解决方案=),如果您添加答案,我可以接受。您也可以使用。
slow\u request
函数不等待任何内容,这意味着尽管它是
async def
,但一旦开始运行,它将阻塞整个事件循环。您应该从请求切换到aiohttp,aiohttp的设计目的是在事件循环等待数据到达时让位于事件循环。解决方案#1完全不是异步的,将阻塞整个事件循环。它不应该是答案的一部分,因为即使它碰巧对你有用,它也不会帮助网站的未来访问者。@user4815162342你能证明你的观点吗?你这么说是真的吗?如果是的话,我就不能接受你的论点。正如我在对问题的评论中提到的,问题在于
slow\u request
没有等待任何东西。Python异步函数是基于协作多任务的,没有
await
的异步函数根本不协作,如果它阻塞,就会阻塞整个事件循环。我并不想表现得咄咄逼人或专横,只是指出了一个我之前已经指出的技术问题,但这仍然是答案。这是你的答案,所以由你决定是否编辑它,但我想警告未来遇到它的访问者。@user4815162342好吧,这些一般原则与解决方案并不矛盾。您可能会设置一个实验,并看到它不会阻止事件循环。您还可以研究asyncio lib并了解