aiohttp默认dns解析拖慢异步请求,因其底层调用同步阻塞的socket.getaddrinfo,导致事件循环停顿、超时频发、延迟波动大;须用aiodns.dnsresolver替换并启用dns缓存。

直接换掉 aiohttp 默认的同步 DNS 解析器,用 aiodns.DNSResolver 替代,并显式开启 DNS 缓存 —— 否则高并发下大量 getaddrinfo 调用会卡死事件循环,延迟波动剧烈,甚至触发假超时。
为什么 aiohttp 默认 DNS 解析会拖慢异步请求
aiohttp 底层调用的是 Python 标准库的 socket.getaddrinfo,这是个同步阻塞函数。哪怕你用 async with session.get(...) 发 100 个请求,只要其中一次 DNS 查询卡住(比如本地 DNS 响应慢、丢包、返回 NXDOMAIN),整个 asyncio 事件循环就停在那儿不动,所有后续协程都得等。
典型现象包括:
-
timeouts频繁抛出,但用curl -v或浏览器直连目标域名却秒开 -
strace -e trace=getaddrinfo,connect显示大量时间耗在getaddrinfo系统调用上 - 同一 Session 并发请求不同域名时延迟方差极大,而只请求单个域名时很稳定
如何正确配置 aiodns + aiohttp 组合
关键不是“手动查完 IP 再拼 URL”,而是让 aiohttp 在建连阶段自动走异步 DNS 流程。这需要替换 TCP 连接器的 resolver 实现:
- 必须传入
aiodns.DNSResolver()实例给aiohttp.TCPConnector(resolver=...),只装aiodns包不配置无效 - 务必显式设置
use_dns_cache=True——aiohttp默认开启缓存,但aiodns.DNSResolver不继承该行为,漏设会导致每次请求都重新查 DNS - 如需指定 DNS 服务器(比如绕过污染或加速),初始化时加
nameservers=['8.8.8.8', '223.5.5.5'] - Windows 用户注意:需安装 Visual C++ Build Tools 或使用预编译 wheel,否则
pip install aiodns可能编译失败
示例代码片段:
import aiohttp
import aiodns
resolver = aiodns.DNSResolver(nameservers=['8.8.8.8'])
connector = aiohttp.TCPConnector(resolver=resolver, use_dns_cache=True)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.get('https://example.com') as resp:
...
常见报错与绕过陷阱
这个组合最常出问题的地方不是功能失效,而是错误被静默吞掉或类型错位:
-
DNSResolveError: Domain name not found:不是网络不通,而是目标域名真没 A/AAAA 记录,或本地 DNS 返回了 NXDOMAIN;可先加raise_for_status=False检查响应状态码和异常类型 -
AttributeError: 'DNSResolver' object has no attribute 'resolve':说明aiodns版本太旧(pip install -U aiodns 升级即可 - 没报错但延迟没改善:检查是否误把
resolver传给了ClientSession而非TCPConnector,或漏写了use_dns_cache=True - 并发量大时出现
Too many open files:需同时调大系统文件描述符限制,并在TCPConnector中设limit=100、limit_per_host=30等参数控流
真正容易被忽略的点是:DNS 缓存开关必须手动打开,且它只作用于 resolver 层;aiohttp 的连接池复用、TLS 会话复用这些优化,全建立在 DNS 解析不拖后腿的前提下 —— 解析慢,后面再快也白搭。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











