httpx 异步支持

2022-07-27 10:37 更新

默认情况下,HTTPX 提供标准的同步 API,但如果需要,还可以选择异步​client​。

Async 是一种并发模型,它比多线程更有效,可以提供显著的性能优势,并允许使用长期存在的网络连接(如 WebSockets)。

如果您使用的是异步 Web 框架,那么您还需要使用异步​client​来发送传出 HTTP 请求。

发出异步请求

要发出异步请求,您需要一个 ​AsyncClient​。

>>> async with httpx.AsyncClient() as client:
...     r = await client.get('https://www.example.com/')
...
>>> r
<Response [200 OK]>
提示
使用 IPython 或 Python 3.8+使用​python -m ​以交互方式尝试此代码,因为它们支持 ​asyncio​在控制台中执行​async ​/​await​表达式。

接口差异

如果您使用的是异步​client​,那么有一些 API 是使用异步方法的。

请求构造

请求方法都是异步的,因此您应该对以下所有内容使用​response = await client.get(...)​样式:

  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)
  • AsyncClient.get(url, ...)

打开和关闭​client

如果需要上下文管理的client,请使用​async with httpx.AsyncClient()​...

async with httpx.AsyncClient() as client:
    ...

或者,如果要显式关闭​client​,请使用​await client.aclose()​:

client = httpx.AsyncClient()
...
await client.aclose()

流式处理响应

AsyncClient.stream(method, url, ...)​方法是一个异步上下文块。

>>> client = httpx.AsyncClient()
>>> async with client.stream('GET', 'https://www.example.com/') as response:
...     async for chunk in response.aiter_bytes():
...         ...

异步响应流式处理方法包括:

  • Response.aread()​- 用于有条件地读取流块内的响应。
  • Response.aiter_bytes()​- 用于将响应内容流式传输为字节。
  • Response.aiter_text()​- 用于将响应内容流式传输为文本。
  • Response.aiter_lines()​- 用于将响应内容流式传输为文本行。
  • Response.aiter_raw()​- 用于流式传输原始响应字节,而不应用内容解码。
  • Response.aclose()​- 用于关闭响应。您通常不需要这样做,因为​.stream​块会在退出时自动关闭响应。

对于上下文块使用不切实际的情况,可以通过使用​client.send(..., stream=True)​ 发送请求实例来进入“手动模式”。

使用 Starlette 将响应转发到流式处理 Web 端点的上下文中的示例:

import httpx
from starlette.background import BackgroundTask
from starlette.responses import StreamingResponse

client = httpx.AsyncClient()

async def home(request):
    req = client.build_request("GET", "https://www.example.com/")
    r = await client.send(req, stream=True)
    return StreamingResponse(r.aiter_text(), background=BackgroundTask(r.aclose))
警告
使用此“手动流式传输模式”时,作为开发人员,您有责任确保最终调用​Response.aclose()​。如果不这样做,连接就会保持打开状态,很可能导致资源泄漏。

流式处理请求

在发送带有​AsyncClient​实例的流式处理请求正文时,应使用异步字节生成器而不是字节生成器:

async def upload_bytes():
    ...  # yield byte content

await client.post(url, content=upload_bytes())

显式transport实例

直接实例化​transport​实例时,需要使用​httpx.AsyncHTTPTransport ​。

例如:

>>> import httpx
>>> transport = httpx.AsyncHTTPTransport(retries=1)
>>> async with httpx.AsyncClient(transport=transport) as client:
>>>     ...

支持的异步环境

HTTPX 支持任一​asyncio​或​trio​作为异步环境。

它将自动检测将这两者中的哪一个用作套接字操作和并发基元的后端。

AsyncIO

AsyncIO 是 Python 的内置库,用于使用 async/await 语法编写并发代码。

import asyncio
import httpx

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

asyncio.run(main())

trio

Trio 是一个替代异步库,围绕结构化并发原则设计。

import httpx
import trio

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

trio.run(main)
重要
必须安装​trio​软件包才能使用 Trio 后端。

AnyIO

AnyIO 是一个异步网络和并发库,可在 ​asyncio​或 ​trio​之上工作。它与所选后端的本机库混合(默认为​asyncio ​)。

import httpx
import anyio

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

anyio.run(main, backend='trio')

调用 Python Web Apps

就像​httpx.Client​允许您直接调用 WSGI Web 应用程序一样,​httpx.AsyncClient​类允许您直接调用 ASGI Web 应用程序。

让我们以这个Starlette应用程序为例:

from starlette.applications import Starlette
from starlette.responses import HTMLResponse
from starlette.routing import Route


async def hello(request):
    return HTMLResponse("Hello World!")


app = Starlette(routes=[Route("/", hello)])

我们可以直接对应用程序发出请求,如下所示:

>>> import httpx
>>> async with httpx.AsyncClient(app=app, base_url="http://testserver") as client:
...     r = await client.get("/")
...     assert r.status_code == 200
...     assert r.text == "Hello World!"

对于一些更复杂的情况,您可能需要自定义 ASGI 传输。这使您可以:

  • 检查 ​500 ​错误响应,而不是通过设置 ​raise_app_exceptions=False​来引发异常。
  • 通过设置​root_path ​在子路径上挂载 ASGI 应用程序。
  • 通过设置​client ​,为请求使用给定的客户端地址。

例如:

# Instantiate a client that makes ASGI requests with a client IP of "1.2.3.4",
# on port 123.
transport = httpx.ASGITransport(app=app, client=("1.2.3.4", 123))
async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
    ...

有关 ​client​和 ​root_path​键的更多详细信息,请参阅 ASGI 文档

启动/关闭 ASGI 应用程序

它不在 HTTPX 的范围内,无法触发应用的生存期事件。

但是,建议将 asgi-lifespan 的​LifespanManager ​与​AsyncClient ​配对使用 。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号