httpx 异步支持
默认情况下,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
配对使用 。