aiohttp批量下载超80万文件后突发WinError 64故障排查
WinError 64: 指定网络名称不再可用 问题排查与解决
问题背景
批量下载超过800,000个文件后程序停止运行,抛出WinError 64错误。所有请求URL均有效可访问,调试发现程序执行session.get()后无响应,未打印后续日志(如Working 4)。
原代码片段
async def binary_coroutines(sem, dl_file_name, url): print(f"Working 1") async with sem: print(f"Working 2") async with aiohttp.ClientSession() as session: print(f"Working 3") async with session.get(url) as resp: print(f"Working 4") r = await resp.read() print(f"Working 5") dl_file_path = os.path.join(REL_DOC_DIR, dl_file_name) with open(dl_file_path, 'wb') as file_pointer: file_pointer.write(r) return dl_file_name
错误日志
Number of documents to be downloaded: 5 Working 1 Working 2 Working 3 Working 1 Working 2 Working 3 Working 1 Working 2 Working 3 Working 1 Working 2 Working 3 Working 1 Working 2 Working 3 [WinError 64] The specified network name is no longer available [WinError 64] The specified network name is no longer available [WinError 64] The specified network name is no longer available [WinError 64] The specified network name is no longer available [WinError 64] The specified network name is no longer available Time Taken: 0.015347826480865478 minutes
问题根因
WinError 64是Windows系统级网络资源耗尽错误,核心触发点是每个协程单独创建aiohttp.ClientSession:
- 每个
ClientSession会初始化独立的连接池、TCP套接字句柄,80万次创建销毁操作会快速耗尽Windows的句柄配额、TCP连接上限 - 系统资源耗尽时,新的HTTP请求无法建立连接,直接抛出「网络名称不可用」错误
解决方案
1. 全局复用ClientSession
aiohttp.ClientSession是协程安全的,支持全局复用,内部连接池会自动管理TCP连接,大幅降低资源占用。修改代码如下:
# 主函数中全局创建Session async def main(): # 控制并发数,建议30-100,根据系统调整 sem = asyncio.Semaphore(50) # 全局复用Session async with aiohttp.ClientSession() as session: # 生成任务列表,传入全局Session tasks = [ binary_coroutines(sem, dl_file_name, url, session) for dl_file_name, url in your_download_list ] # 处理任务结果 for task in asyncio.as_completed(tasks): try: downloaded_file = await task print(f"完成下载:{downloaded_file}") except Exception as e: print(f"下载失败:{str(e)}") # 协程接收外部传入的Session,不再单独创建 async def binary_coroutines(sem, dl_file_name, url, session): print(f"Working 1") async with sem: print(f"Working 2") print(f"Working 3") try: # 设置超时时间,避免请求挂起占用资源 async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp: print(f"Working 4") # 检查HTTP响应状态码,捕获4xx/5xx错误 resp.raise_for_status() r = await resp.read() print(f"Working 5") dl_file_path = os.path.join(REL_DOC_DIR, dl_file_name) with open(dl_file_path, 'wb') as file_pointer: file_pointer.write(r) return dl_file_name except Exception as e: # 针对资源错误添加重试逻辑,最多重试2次 for retry in range(2): try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp: resp.raise_for_status() r = await resp.read() dl_file_path = os.path.join(REL_DOC_DIR, dl_file_name) with open(dl_file_path, 'wb') as file_pointer: file_pointer.write(r) return dl_file_name except: continue # 重试失败后抛出原错误 raise e
2. 合理控制并发数
Windows系统默认TCP连接数、句柄数有限,建议将Semaphore的并发数设置在30-100区间,避免过高并发直接打满系统资源。
3. 批量拆分与资源回收(可选)
针对80万+的超大规模下载,可将任务拆分为多个批次(比如每10000个文件为一批),每完成一批后调用await asyncio.sleep(1),让系统有时间回收闲置的网络资源。
4. 排查系统资源限制
可通过Windows任务管理器的「性能-打开资源监视器-句柄」查看进程句柄数,若接近系统上限(默认通常为16384),可通过注册表调整HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota扩大配额(需重启系统生效)。
内容的提问来源于stack exchange,提问作者Sudipto Ray
相关产品推荐
相关产品推荐

