如何从我的 FastAPI 应用程序向另一个站点 (API) 发送 HTTP 请求?
Posted
技术标签:
【中文标题】如何从我的 FastAPI 应用程序向另一个站点 (API) 发送 HTTP 请求?【英文标题】:How can I send an HTTP request from my FastAPI app to another site (API)? 【发布时间】:2020-12-31 12:05:20 【问题描述】:我正在尝试使用以下代码 sn-p 一次向服务器 http://httpbin.org/uuid
发送 100 个请求
from fastapi import FastAPI
from time import sleep
from time import time
import requests
import asyncio
app = FastAPI()
URL= "http://httpbin.org/uuid"
# @app.get("/")
async def main():
r = requests.get(URL)
# print(r.text)
return r.text
async def task():
tasks = [main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main(),main()]
# print(tasks)
# input("stop")
result = await asyncio.gather(*tasks)
print (result)
@app.get('/')
def f():
start = time()
asyncio.run(task())
print("time: ",time()-start)
我将 FastAPI 与 Asyncio 一起使用,以实现大约 3 秒或更短的最短时间,但使用上述方法,我得到的总时间为 66 秒,超过一分钟。我还想保留main
函数以对r.text
进行其他操作。我知道要达到如此短的时间,需要并发,但我不确定我在这里犯了什么错误。
【问题讨论】:
即使使用 curl 我得到 0.111 毫秒的 100 个请求time for _ in 1..100; do curl http://httpbin.org/uuid & done
这很奇怪。
这是因为每个 curl 都拆分为不同的进程,但同样的事情在我的代码中不起作用 - imgur.com/a/pmh7qLb time for _ in 1..100; do curl http://httpbin.org/uuid & done && ps aux | grep curl
IKR 我在说这个,asyncio 的行为应该和 curl 完全一样,我在我的机器上尝试了不同的方法,但也没有成功,然后我想到 uvicorn 可能只是另一个事件循环就是这个问题,之后我正常运行了,也花了这么长时间..
我也在使用这种模式进行异步,这对我来说看起来很可靠,我创建了一个 gist 你可能想看看
你也可以试试python-httpx.org。使用基本 url 创建一个 AsyncClient,然后使用客户端(相当于 request
会话)对 url 执行 100 次调用
【参考方案1】:
requests
是一个同步库。您需要使用基于asyncio
的库来异步发出请求。
httpx
httpx.AsyncClient
通常在 FastAPI 应用程序中用于请求外部服务。它也用于应用程序的asynchronous tests。默认使用它。
from fastapi import FastAPI
from time import time
import httpx
import asyncio
app = FastAPI()
URL = "http://httpbin.org/uuid"
async def request(client):
response = await client.get(URL)
return response.text
async def task():
async with httpx.AsyncClient() as client:
tasks = [request(client) for i in range(100)]
result = await asyncio.gather(*tasks)
print(result)
@app.get('/')
async def f():
start = time()
await task()
print("time: ", time() - start)
输出
['\n "uuid": "65c454bf-9b12-4ba8-98e1-de636bffeed3"\n\n', '\n "uuid": "03a48e56-2a44-48e3-bd43-a0b605bef359"\n\n',...
time: 0.5911855697631836
aiohttp
aiohttp
也可用于 FastAPI 应用程序,但如果您确实需要,请这样做。
from fastapi import FastAPI
from time import time
import aiohttp
import asyncio
app = FastAPI()
URL = "http://httpbin.org/uuid"
async def request(session):
async with session.get(URL) as response:
return await response.text()
async def task():
async with aiohttp.ClientSession() as session:
tasks = [request(session) for i in range(100)]
result = await asyncio.gather(*tasks)
print(result)
@app.get('/')
async def f():
start = time()
await task()
print("time: ", time() - start)
如果你想限制并行执行的请求数,你可以像这样使用asyncio.semaphore
:
MAX_IN_PARALLEL = 10
limit_sem = asyncio.Semaphore(MAX_IN_PARALLEL)
async def request(client):
async with limit_sem:
response = await client.get(URL)
return response.text
【讨论】:
您可能希望在task
中创建一个会话并将其传递给main()
。这将使 aiohttp 更有效,例如重用 TCP 连接。
"它是 FastAPI 的依赖项。" - 不是。
这只是fastapi[test]
的依赖,所以我删除了这句话以避免混淆。
@任何理由使用 httpx?与 aiohttp 相比?我问是因为 httpx 被认为是 beta 版本,所以在生产中,你想要“更”稳定的版本,thx
@AlexJolig 您链接到的page 的日期为2021-04-17。该答案的日期为 2020-09-14。该页面的标题与此问题相同。也许您应该将您的评论指向该网站的维护者,并收回您对该答案作者的指控。【参考方案2】:
@Alex Noname 很好地使用了异步请求库。如果你想让代码更快,我建议使用 asyncio.Queue 作为替代。在这个例子中,我创建了 100 个生产者和 100 个消费者。您可以限制队列中的最大消息数,例如,生产者等待直到有空间容纳新消息
asyncio.Queue(maxsize=100)
我也使用了 httpx 中的 AsyncClient。
如果您想了解有关队列的更多信息,我建议您阅读这篇文章 https://realpython.com/async-io-python/
from time import time
from typing import List
from fastapi import FastAPI
from httpx import AsyncClient
app = FastAPI()
URL = "http://httpbin.org/uuid"
client = AsyncClient()
async def main():
r = await client.get(URL)
return r.text
async def producer(queue: asyncio.Queue):
await queue.put(main)
async def consumer(queue: asyncio.Queue, resp: List):
# await queue.get() == main -> without arguments
resp.append(await (await queue.get())())
async def task():
q = asyncio.Queue(maxsize=100)
response = []
consumers = []
producers = []
[consumers.append(consumer(q, response)) for c in range(100)]
[producers.append(producer(q)) for p in range(100)]
await asyncio.gather(*producers)
await asyncio.gather(*consumers)
print(response)
@app.get('/')
def f():
start = time()
asyncio.run(task())
print("time: ", time() - start)
if __name__ == '__main__':
f()
输出
['\n "uuid": "a7713d07-ea5d-40d3-95b4-6673f3c50a8b"\n\n', '\n "uuid": "c93f8b89-2c44-40fa-9e5f-736e22ad5f23"\n\n', '\n "uuid": "cbb4ad76-7790-45ae-87f1-e425eddc8021"\n\n', '\n "uuid": "4c1d81c0-ae7d-401a-99df-e98af3651335"\n\n', '\n "uuid": "c5f70738-fbba-4cf9-8fdf-29f8b4eabe63"\n\n', '\n "uuid": "d016b852-4312-4502-a336-a6a110237d1d"\n\n', '\n "uuid": "22d8b00b-4266-4236-b5a3-ed5d7c5be416"\n\n', '\n "uuid": "cd54fdbb-7de9-4df3-90cc-e6b108d5fdf8"\n\n', '\n "uuid": "757f0a26-7896-4a04-bea2-60c66a38b05b"\n\n', '\n "uuid": "72eb6584-21f4-449b-b6bd-d0f88666126f"\n\n', '\n "uuid": "b3deadf5-5b79-491b-829c-0404c306cb68"\n\n', '\n "uuid": "789e7422-493d-49d2-9585-e5ca34b7cf36"\n\n', '\n "uuid": "48d29a82-ff7c-41f5-8af2-42784326a31f"\n\n', '\n "uuid": "84b2d67c-331c-4037-b6e4-c299d93c1899"\n\n', '\n "uuid": "386e79f9-073a-4f27-961c-7befcdf95cd4"\n\n', '\n "uuid": "8dfdb5e4-dd69-4043-b174-48ec8505f36f"\n\n', '\n "uuid": "633e634b-b107-42bb-a7d3-c6bbfff089a0"\n\n', '\n "uuid": "962d665f-8663-4be7-a3c6-9426ba500bf4"\n\n', '\n "uuid": "320fb858-a751-4c34-9cdb-ddd2f4e28efa"\n\n', '\n "uuid": "46a75693-5255-4ac7-8d7a-54910b4d6f68"\n\n', '\n "uuid": "5323734b-7ff9-455e-ba5a-66383e6b9a1f"\n\n', '\n "uuid": "622a579f-35b6-4e4b-9dba-a8a69c2049c8"\n\n', '\n "uuid": "593d5e82-cef3-4be0-99ab-e3034855d7a1"\n\n', '\n "uuid": "80f139df-2a27-40c1-8329-e4faa035c45c"\n\n', '\n "uuid": "a97e084c-4d30-4c7b-a96e-89ed00dcfe2a"\n\n', '\n "uuid": "360d49eb-7222-4064-81c2-6eba2d43a9a5"\n\n', '\n "uuid": "a81b6eab-a646-4e58-b986-96a90baa52aa"\n\n', '\n "uuid": "0160337e-b400-41d6-ae89-aa46c5131f40"\n\n', '\n "uuid": "e600722f-8c15-4959-948b-4c4e5296feb2"\n\n', '\n "uuid": "f15403e4-3674-43b2-a0c9-649fd828ba7e"\n\n', '\n "uuid": "36bf139c-cc18-45a8-bc55-e7f90ce290b5"\n\n', '\n "uuid": "b2368a3c-d86b-4fcd-a0d3-bf7f8f657a83"\n\n', '\n "uuid": "d9f16c36-3572-4c70-8a41-3d4e279d76bf"\n\n', '\n "uuid": "796087cc-a202-40dd-9921-14802a73323d"\n\n', '\n "uuid": "089fa0d7-4c48-4daa-a80d-cb5ebd37dfb7"\n\n', '\n "uuid": "e5582bc7-0f8a-4da7-b640-79a0d812154d"\n\n', '\n "uuid": "bac0640b-0d0b-4bf2-a3c1-36bdda7cce03"\n\n', '\n "uuid": "b4353004-02b2-4846-8692-33dd77ad1d3f"\n\n', '\n "uuid": "1b34a744-d0ea-4acf-8bda-33743800d86a"\n\n', '\n "uuid": "4d9dd269-6ee2-4356-9bc4-ddf188445320"\n\n', '\n "uuid": "a1f380df-0c0d-4aee-bbb7-c3e99fbfe54f"\n\n', '\n "uuid": "7cb762eb-1a42-433d-97ea-aa9de4504e35"\n\n', '\n "uuid": "981c40e2-64bf-4746-8103-9430bda2a5ca"\n\n', '\n "uuid": "22b778eb-82d1-48b9-9874-5ebb80ddb8b1"\n\n', '\n "uuid": "e7a9e0e8-7964-400c-aafe-9c36b9b7e1a0"\n\n', '\n "uuid": "21a59b91-2732-4bb6-a47e-84008a03c20c"\n\n', '\n "uuid": "a78eeb39-5ecb-4509-87c2-b4a2529e3536"\n\n', '\n "uuid": "4a332579-ce03-4f69-9db5-78da9196d6b2"\n\n', '\n "uuid": "55fbc34f-4eb3-4356-98e3-1df38054a4b2"\n\n', '\n "uuid": "257ac454-09c2-4fd4-bdb3-303495360fa2"\n\n', '\n "uuid": "7505cc0d-01b3-47f8-91d4-3e54d0f387de"\n\n', '\n "uuid": "0fd67af2-622e-4688-b3c8-f64e20f1f3ec"\n\n', '\n "uuid": "07653ccf-f408-4807-8ff5-e6098d657451"\n\n', '\n "uuid": "b9d0ff18-fd67-4afa-adbe-ebcb53380804"\n\n', '\n "uuid": "70d4d53b-2f06-41be-bb38-47f010cfa40f"\n\n', '\n "uuid": "a6d49873-e749-4578-ae9c-e6c6f473535d"\n\n', '\n "uuid": "e67efee5-76ad-4812-bb97-016ef9ff87e8"\n\n', '\n "uuid": "67886926-b2d9-44fb-b836-26b81c53e5fb"\n\n', '\n "uuid": "dcbd4ff8-e3cd-4e03-b12d-5fb3834b0e00"\n\n', '\n "uuid": "65c2eaee-5fa2-4b58-a1c3-adeb04d92c71"\n\n', '\n "uuid": "2cee4ec9-952e-45c5-91b7-f4f5848c3455"\n\n', '\n "uuid": "8e94bf1c-ee5a-483a-a962-d0b9aea48c95"\n\n', '\n "uuid": "c1fe17bc-bedf-4c4c-952d-a5921f693d9f"\n\n', '\n "uuid": "221456fd-48ca-4826-a8b5-5fa0b23db6e4"\n\n', '\n "uuid": "62fda759-b382-44e4-ad7d-d19a952fc1c7"\n\n', '\n "uuid": "73faeb91-215e-4e49-8f11-11b98e499cc7"\n\n', '\n "uuid": "f3279c45-ebcc-4079-b823-3efe825c7cf8"\n\n', '\n "uuid": "b892672b-4510-44f4-b61e-9cccaa52421e"\n\n', '\n "uuid": "8926979d-71a7-4171-9389-ddafff89e229"\n\n', '\n "uuid": "d97cef59-4862-42ca-b0f2-261f98fd4b6f"\n\n', '\n "uuid": "3362ff93-89e4-4889-a2f2-2e03771e86ce"\n\n', '\n "uuid": "9f525251-4fe4-4a9c-97b5-2f01d2b37aaf"\n\n', '\n "uuid": "036959d4-3179-40f9-bbf3-32274f2cede2"\n\n', '\n "uuid": "157f8c22-6214-4e27-ab5d-08d39f96d1d3"\n\n', '\n "uuid": "e4bfbf62-7c33-4fd7-a231-47f5ce398041"\n\n', '\n "uuid": "a41512c1-3346-4457-a379-64d690ffc2ea"\n\n', '\n "uuid": "7bb07cfb-294b-44fa-a8dc-6d283c54409f"\n\n', '\n "uuid": "f2297d22-a2d0-47ff-8d65-24c6fe7877a7"\n\n', '\n "uuid": "645e255b-4c93-4c8f-9ff2-43da293db660"\n\n', '\n "uuid": "9190e370-dfa9-47a6-8cef-8df7ab762433"\n\n', '\n "uuid": "83216551-9f1b-48b2-8cd6-fd125a7ce965"\n\n', '\n "uuid": "aaddb98c-879b-472d-aa39-1a684ef7d179"\n\n', '\n "uuid": "4bd7e2fd-1453-4433-aa9f-bc29d82f5b9d"\n\n', '\n "uuid": "b02d65e8-2063-4060-96af-088ec497fc10"\n\n', '\n "uuid": "e10e3dd2-83c5-4595-afe4-4145bce79193"\n\n', '\n "uuid": "8cb62784-1b5d-4dcc-8342-02ad7d417ca9"\n\n', '\n "uuid": "13ef1509-4f69-4426-ac42-cb29a2d0f094"\n\n', '\n "uuid": "4d4571d5-69bb-4625-b246-b5eef50aa10d"\n\n', '\n "uuid": "75e7a2ca-bfa8-43b9-b33a-f3f927453579"\n\n', '\n "uuid": "0a8cc8ff-2039-4873-9e38-afad3e10d726"\n\n', '\n "uuid": "189ae75b-4879-4897-9725-f9be17e49844"\n\n', '\n "uuid": "ba482468-f45f-4060-a0c1-3ef31bb283c8"\n\n', '\n "uuid": "3809f1c7-2f11-487d-bf96-8abf64e08298"\n\n', '\n "uuid": "da5ea88b-974d-4238-9654-ac56c657c8b4"\n\n', '\n "uuid": "edc3de79-7cf4-42a3-a5f4-b754136a6fd3"\n\n', '\n "uuid": "6f5ecd91-537c-4009-8435-6c31ce035d36"\n\n', '\n "uuid": "4a33b29d-78ba-468f-8f30-a01b3d9e2a87"\n\n', '\n "uuid": "a5a2ef2d-d4a2-48e1-8335-f8c1309328c4"\n\n', '\n "uuid": "3d1679da-afdd-4f04-9c16-0aaea4c53d0c"\n\n', '\n "uuid": "c4025845-0d4c-4549-acb8-1a249b33e644"\n\n']
time: 1.0535461902618408
【讨论】:
你能解释一下这段代码比the other answer更快吗?在该示例中,可能需要避免几个编程缺陷,例如将项目附加到理解列表中的外部列表,而不是直接使用理解列表。此外,当q
的maxsize
低于100 时,代码将永远挂起,因为生产者最终将挂在queue.put()
上,因此gather(*producers)
永远不会完成。您必须将这两个集合安排为独立的任务,但这不会发生在这里。投反对票。
你在解决什么问题?以上是关于如何从我的 FastAPI 应用程序向另一个站点 (API) 发送 HTTP 请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何防止从我的角度站点直接访问 bootstrap.min.js?
WAMP Laravel - 从一个本地站点向另一个站点发送API请求混合了环境变量
跨站点 cookie 的问题:如何将 cookie 从后端设置到前端