如何从我的 FastAPI 应用程序向另一个站点 (API) 发送 HTTP 请求?

joh*_*ich 19 python httprequest async-await python-asyncio fastapi

我正在尝试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)
Run Code Online (Sandbox Code Playgroud)

我将 FastAPI 与 Asyncio 结合使用,以实现大约 3 秒或更短的最短时间,但使用上述方法我得到的总时间为 66 秒,超过一分钟。我还想保留main用于附加操作的功能r.text。我知道要实现如此短的时间,需要并发性,但我不确定我在这里犯了什么错误。

ale*_*ame 30

requests是一个同步库。您需要使用asyncio基于库来异步发出数百个请求。

httpx

httpx.AsyncClient通常在 FastAPI 应用程序中用于请求外部服务。它还用于应用程序的异步测试。默认使用它。

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)
Run Code Online (Sandbox Code Playgroud)

输出

['{\n  "uuid": "65c454bf-9b12-4ba8-98e1-de636bffeed3"\n}\n', '{\n  "uuid": "03a48e56-2a44-48e3-bd43-a0b605bef359"\n}\n',...
time:  0.5911855697631836
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

如果要限制并行执行的请求数,可以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
Run Code Online (Sandbox Code Playgroud)

  • @有什么理由使用httpx吗?与 aiohttp 相比?我问是因为 httpx 被认为是 beta 版本,所以在生产中,你想要“更”稳定的版本,thx (3认同)
  • 您可能想在“task”中创建一个会话并将其传递给“main()”。这将使 aiohttp 通过重用 TCP 连接等方式变得更加高效。 (2认同)