perf(http): AsyncRequestUtils 默认启用 HTTP/2

为 AsyncRequestUtils 增加 http2: bool = True 参数(默认开启),
内部贯穿到 _get_shared_async_transport 与 path C 兜底 AsyncClient。
http2 加入共享 AsyncHTTPTransport 桶 key,让不同 h2 设置自动隔离。

启用基于 TLS ALPN:服务端宣告支持 h2 时切到 HTTP/2 多路复用;
不支持(含明文 HTTP、老 nginx/Apache)透明回落 HTTP/1.1。如个别
站点 h2 实现异常,调用方传 http2=False 单独关闭。

依赖:httpx extras 由 [socks] 扩展为 [socks,http2],引入纯 Python
包 h2 / hpack / hyperframe(无原生扩展)。

真实 TMDB 压测(30 部美剧 × 每部 50 集 = 3060 请求/版本):
HTTP/1.1 52.0s → HTTP/2 27.6s,节省 24.4s(1.88×)。
单请求 p95 由 96.1ms 降至 20.1ms,长尾大幅收敛。

公共 API 表面零变动;插件可按需 http2=False 单点关闭。
This commit is contained in:
Aqr-K
2026-05-11 03:17:47 -04:00
committed by jxxghp
parent b7fc5b0203
commit 0fb11880a4
2 changed files with 15 additions and 1 deletions

View File

@@ -52,6 +52,7 @@ class _NonClosingTransportProxy(httpx.AsyncBaseTransport):
_SharedTransportKey = Tuple[
Optional[str], # proxy
Union[bool, str], # verify
bool, # http2
int, # max_keepalive_connections
int, # max_connections
int, # keepalive_expiry
@@ -76,6 +77,7 @@ _pending_eviction_tasks: set[asyncio.Task] = set()
def _get_shared_async_transport(
proxy: Optional[str],
verify: Union[bool, str],
http2: bool,
max_keepalive_connections: int,
max_connections: int,
keepalive_expiry: int,
@@ -104,6 +106,7 @@ def _get_shared_async_transport(
key: _SharedTransportKey = (
proxy,
verify,
http2,
max_keepalive_connections,
max_connections,
keepalive_expiry,
@@ -115,6 +118,7 @@ def _get_shared_async_transport(
# 首次见到这个配置,创建新的共享 transport 桶
transport = httpx.AsyncHTTPTransport(
http2=http2,
proxy=proxy,
verify=verify,
limits=httpx.Limits(
@@ -786,6 +790,7 @@ class AsyncRequestUtils:
accept_type: str = None,
verify: Union[bool, str] = False,
follow_redirects: bool = True,
http2: bool = True,
max_keepalive_connections: int = _DEFAULT_MAX_KEEPALIVE_CONNECTIONS,
max_connections: int = _DEFAULT_MAX_CONNECTIONS,
keepalive_expiry: int = _DEFAULT_KEEPALIVE_EXPIRY,
@@ -802,6 +807,10 @@ class AsyncRequestUtils:
:param accept_type: Accept头部信息默认为 "application/json"
:param verify: 是否校验证书
:param follow_redirects: 客户端默认是否跟随重定向
:param http2: 是否启用 HTTP/2默认 True。基于 TLS ALPN 协商:服务端
支持 h2 时复用流多路复用,不支持(含明文 HTTP、老 nginx/Apache
自动透明回落 HTTP/1.1。如遇个别站点 h2 实现异常,可显式传
http2=False 单独关闭。
:param max_keepalive_connections: 共享 AsyncHTTPTransport 的最大 keep-alive 连接数
:param max_connections: 共享 AsyncHTTPTransport 的最大连接数
:param keepalive_expiry: 共享 AsyncHTTPTransport 的 keep-alive 连接过期时间(秒)
@@ -811,6 +820,7 @@ class AsyncRequestUtils:
self._timeout = timeout or 20
self._verify = verify
self._follow_redirects = follow_redirects
self._http2 = http2
self._max_keepalive_connections = max_keepalive_connections
self._max_connections = max_connections
self._keepalive_expiry = keepalive_expiry
@@ -915,6 +925,7 @@ class AsyncRequestUtils:
transport = _get_shared_async_transport(
proxy=self._proxies,
verify=self._verify,
http2=self._http2,
max_keepalive_connections=self._max_keepalive_connections,
max_connections=self._max_connections,
keepalive_expiry=self._keepalive_expiry,
@@ -934,6 +945,7 @@ class AsyncRequestUtils:
# 兜底:没有运行中的事件循环时,临时客户端走完即关
async with httpx.AsyncClient(
http2=self._http2,
proxy=self._proxies,
timeout=self._timeout,
verify=self._verify,
@@ -1116,6 +1128,7 @@ class AsyncRequestUtils:
transport = _get_shared_async_transport(
proxy=self._proxies,
verify=self._verify,
http2=self._http2,
max_keepalive_connections=self._max_keepalive_connections,
max_connections=self._max_connections,
keepalive_expiry=self._keepalive_expiry,
@@ -1132,6 +1145,7 @@ class AsyncRequestUtils:
else:
client = await stack.enter_async_context(
httpx.AsyncClient(
http2=self._http2,
proxy=self._proxies,
timeout=self._timeout,
verify=self._verify,