aiohttp.ClientSession 必须复用,因新建会重复初始化连接池、SSL 上下文并绑定事件循环,导致开销大、RuntimeError、连接泄漏及文件描述符耗尽;应全局单例创建,用 async with 包裹单次请求。
aiohttp.ClientSession 必须复用,不能每次请求都新建因为新建 ClientSession 会重新初始化连接池、SSL 上下文和事件循环绑定,不仅开销大,还会导致 RuntimeError: Session is closed 或连接泄漏。尤其在高并发场景下,频繁创建销毁 session 容易触发文件描述符耗尽(OSError: [Errno 24] Too many open files)。
实操建议:
ClientSession,用 async with 仅包裹单次请求逻辑,不是整个 session 生命周期
timeout 和 headers 参数,而非每个 session.get() 都重复指定aiohttp 中如何正确处理 JSON 响应和编码问题直接调用 response.json() 看似方便,但默认不校验 Content-Type,且对非 UTF-8 编码(如 GBK、ISO-8859-1)会抛 UnicodeDecodeError。更隐蔽的问题是:某些 API 返回 text/plain 但内容实为 JSON 字符串,此时 .json() 会静默失败或解析出错。
实操建议:
await response.text(encoding="utf-8") 拿原始字符串,再用 json.loads() 显式解析,便于捕获和调试格式错误charset=gbk,必须显式传 encoding:await response.text(encoding="gbk"),不能依赖自动探测response.content_type 和 response.charset 调试,再决定解析路径asyncio.Semaphore 还是 aiohttp.TCPConnector 的 limit
两者都管并发,但作用层级不同:TCPConnector(limit=10) 控制的是底层 TCP 连接数上限,影响复用和端口占用;Semaphore 控制的是协程并发数,影响任务调度节奏。混用时若配置冲突(比如 semaphore=5 但 connector.limit=100),实际并发仍受更严的那个限制。
实操建议:
TCPConnector(limit=20, limit_per_host=5) —— 它能真正防止对单个 host 建连风暴,比纯协程限流更贴近网络层实际压力Semaphore,且注意把它定义在 session 外部,避免每次请求都 new 一个新实例limit=0(无限制),Linux 默认单进程最大 socket 数约 1024,超出会报 OSError: [Errno 24]
aiohttp 的异常分散在多个层级:DNS 解析失败(aiohttp.ClientConnectorError)、连接超时(asyncio.TimeoutError)、HTTP 状态码非 2xx(response.raise_for_status() 抛 aiohttp.ClientResponseError)。漏掉任意一类,都会让程序意外中断。
实操建议:
try/except (aiohttp.ClientError, asyncio.TimeoutError) as e: 捕获网络层异常response 显式调用 response.raise_for_status(),否则 404、502 等状态码不会抛异常,容易误判为成功str(e) 和 response.url if "response" in locals() else None,否则排查时不知道失败发生在哪个 URL