使用httpx AsyncClient发送请求时间歇性出现ClosedResourceError咨询
httpx 0.19.0 AsyncClient并发请求抛出ClosedResourceError的问题解答
问题确认与已知情况
这个ClosedResourceError异常确实有不少开发者在使用httpx 0.19.0版本的AsyncClient并发发送HTTP/2请求时遇到过,属于该版本的已知bug。
问题根源在于当时的HTTP/2连接池实现存在资源管理缺陷:当连接被服务器意外关闭或超时后,连接池没有及时将失效连接标记并剔除,后续并发请求复用了已关闭的连接,导致写入数据时触发资源已关闭的异常。
修复状态
该问题在httpx 0.20.x及后续版本中已被官方修复,主要优化了:
- HTTP/2连接状态的实时检测逻辑
- 失效连接的自动回收与剔除机制
- 并发请求场景下的连接池资源分配策略
升级到0.20.0及以上版本后,这类异常基本不会再出现。
临时解决方案(无法立即升级时)
如果暂时无法升级httpx版本,可以尝试以下临时方案:
- 在请求逻辑中捕获
ClosedResourceError,添加重试机制,重试时重新获取可用连接 - 限制并发请求的数量,避免连接池过载导致连接被强制关闭
- 若业务允许,暂时禁用HTTP/2,改用HTTP/1.1(初始化AsyncClient时设置
http2=False)
代码优化建议
原代码中手动管理AsyncClient的生命周期存在潜在风险,建议改用上下文管理器async with自动处理连接的创建与关闭,同时优化并发请求的逻辑:
async def send_request(self, payload, times, method, uri, headers): # 使用上下文管理器自动管理client生命周期 async with httpx.AsyncClient( http2=True, http1=False, proxies=self.proxies, timeout=10 ) as conn: tasks = [ self.async_send_request(conn, method, self.dest_host, self.dest_port, uri, payload=payload, headers=headers) for _ in range(int(times)) ] resp = await asyncio.gather(*tasks) log.logger.info("[+] @{} {} {}".format(method, resp[-1].status_code, uri)) return resp[-1] async def async_send_request(self, conn, method, dest_host, dest_port, uri, payload=None, headers=None): url_scheme = "https" if self.security == 'secure' else "http" url = f"{url_scheme}://{dest_host}:{dest_port}{uri}" return await conn.request( method, url, data=payload, headers=headers, allow_redirects=False ) def send_message(self, payload, times, method, uri, headers): response = asyncio.run(self.send_request(payload, times, method, uri, headers)) return response
优化点说明:
- 使用
async with创建AsyncClient,确保请求完成后自动关闭连接,避免手动管理的疏漏 - 将AsyncClient实例传入请求方法,避免在类实例中持有可能失效的连接引用
- 使用f-string简化URL拼接,提升代码可读性
内容的提问来源于stack exchange,提问作者Amit Jain
相关产品推荐
相关产品推荐

