Python 3.11 中用 aiohttp 实现高并发,关键必须显式配置 ClientTimeout、TCPConnector 和 asyncio.Semaphore:ClientTimeout 分层设超时(如 total=10, connect=3, sock_read=5),TCPConnector 调优连接池(如 limit=1000, limit_per_host=100, ttl_dns_cache=10),Semaphore 硬控并发数(如 500),三者缺一不可,否则 QPS 卡在几百并易触发限流或端口耗尽。

直接上结论:Python 3.11 中用 aiohttp 实现高并发,关键不是写多少 await,而是三件事必须显式配——ClientTimeout、TCPConnector、asyncio.Semaphore。缺一个,QPS 就卡在几百,还容易触发目标限流或本地端口耗尽。
为什么默认 ClientSession 一并发就卡住?
不是代码没写对,是 aiohttp 默认配置压根不为高并发设计:
-
TCPConnector(limit=100, limit_per_host=30):全局最多 100 个连接,单域名最多 30 个——发 1000 个请求,实际同时跑的可能就二三十个,其余全在排队 - DNS 缓存默认
ttl_dns_cache=300(5 分钟):域名解析结果长期不更新,万一目标切了 IP,你还在连旧地址 - 超时全靠系统默认(约 5 分钟):一个 DNS 卡住或服务端假死,整批请求都得等它超时,拖垮吞吐
- Python 3.11 虽默认启用
uvloop(需手动装),但没显式调用uvloop.install(),事件循环仍是标准 asyncio,P99 延迟高一截
怎么配 TCPConnector 才不翻车?
连接池参数不是越大越好,要平衡目标服务容忍度和本地资源:
-
limit=0或limit=1000:设为 0 表示不限制总连接数;生产环境建议设为略高于预期并发数(比如目标扛 800 QPS,这里设 1000) -
limit_per_host=100:单域名上限别超过 100,否则大概率被 nginxlimit_conn或 Cloudflare 429 拦住 -
ttl_dns_cache=10:DNS 缓存缩到 10 秒,适应云服务动态 IP 变更 -
keepalive_timeout=30:空闲连接保持 30 秒,复用更充分;低于 15 秒容易频繁重连 -
force_close=False:必须关掉,否则连接池失效,每次都是新 TCP 握手
示例:
connector = aiohttp.TCPConnector(limit=1000, limit_per_host=100, ttl_dns_cache=10, keepalive_timeout=30)
立即学习“Python免费学习笔记(深入)”;
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
asyncio.Semaphore 是怎么控并发的?
它才是真正的“闸门”,管的是**同时发起的请求数**,和连接池无关:
- 信号量值设为 500,意味着最多 500 个
session.get()同时执行;第 501 个会自动 await 等前面任一完成 - 必须用
async with sem:包裹请求逻辑,不能只在gather外层套一层——那样起不到节流作用 - 别用
asyncio.gather(*tasks)直接扔 1000 个任务进去,那等于把闸门拆了 - 配合随机 jitter:
await asyncio.sleep(random.uniform(0.01, 0.05)),防请求毛刺打穿对方限流阈值
ClientTimeout 怎么分层设才合理?
单设 total 不够,建连慢和读响应慢要分开控:
-
connect=3:DNS 解析 + TCP 握手 + TLS 协商必须在 3 秒内完成,超时立刻换连接 -
sock_read=5:响应体读取最多 5 秒,避免大文件传输卡死 -
total=10:整个请求生命周期上限,兜底防止异常累积 - 别用
None或过长值(如 60),Python 3.11 下 asyncio 调度延迟敏感,长等待会放大协程堆积
示例:
timeout = aiohttp.ClientTimeout(total=10, connect=3, sock_read=5)
最容易被忽略的点:ClientSession 必须复用,绝不能在每个请求里新建;uvloop.install() 要在 asyncio.run() 前调用;orjson 替换 json 对响应构造提速明显,但遇到 datetime 得先 str()——这些细节不处理,性能瓶颈永远在 CPU 或序列化上,而不是网络本身。

















