高性能硬件环境下能否1秒内向10万个网站发送异步请求?线程池代码资源利用率低如何优化?
问题分析与解决方案
首先咱们先拆解你遇到的核心问题:
- 你用的
requests是同步阻塞IO库,配合ThreadPoolExecutor本质是用线程模拟并发,并非真正的异步IO。当线程数拉到极高(比如10万URL就开5万线程),系统会把大量资源消耗在线程上下文切换上,这就是前1000次快、后续越来越慢的根本原因——线程爆炸导致的资源浪费。 - 另外,
requests默认HTTP连接池大小只有10,就算开了大量线程,大部分线程都在等待连接池释放连接,进一步放大了性能瓶颈。
能不能1秒内向100,000个网站发送异步请求?
理论上有实现可能,但需要满足几个关键前提:
- 带宽足够:1Gbit/s换算为字节是约125MB/s,如果每个请求的HTTP头+内容平均为1KB,10万请求总大小是100MB,刚好在带宽承载范围内;若请求内容更大,带宽会成为瓶颈。
- 系统参数调优:默认的Linux/Windows系统对并发TCP连接、文件描述符的限制极低(比如Linux默认文件描述符仅1024),必须先调整这些参数才能支撑10万并发:
- Linux下临时生效:
ulimit -n 100000,永久生效需修改/etc/security/limits.conf - 调整TCP内核参数,比如开启
tcp_tw_reuse、增大tcp_max_syn_backlog等,减少TIME_WAIT连接占用
- Linux下临时生效:
- 目标服务器的响应能力:如果大部分目标网站服务器响应慢或拒绝高频请求,你的请求成功率会下降,但发送请求的速度仍能达标。
真正的异步解决方案:使用aiohttp
替换requests和线程池为异步IO库aiohttp,它基于asyncio实现真正的非阻塞IO,只用少量线程就能处理上万并发请求,完全能发挥你的硬件性能。
优化后的代码示例
import asyncio import aiohttp import time async def fetch(session, url): try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=4)) as response: # 若无需响应内容,可直接返回,不用await response.text(),能进一步提速 return await response.text() except Exception as e: return "TIMEOUT ERR" async def main(): list_of_urls = [] with open("urllist.txt","r") as readurl: for i in readurl.readlines(): cleaned_url = f"http://{i.replace('\n','').strip()}" list_of_urls.append(cleaned_url) # 配置连接池,增大并发连接数(根据系统承受能力调整) connector = aiohttp.TCPConnector( limit=10000, ttl_dns_cache=300, enable_cleanup_closed=True ) async with aiohttp.ClientSession(connector=connector) as session: # 创建所有异步任务 tasks = [fetch(session, url) for url in list_of_urls] # 并发执行所有任务 await asyncio.gather(*tasks) if __name__ == "__main__": start_time = time.time() # Windows系统需添加以下一行: # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(main()) print(f"总耗时: {(time.time() - start_time):.2f} 秒")
额外优化建议
- 分批次处理:如果10万请求一次性发起,可能瞬间打满系统网络栈,可以把URL分成多批次(比如每批1万)依次处理,避免瞬间压力过大。
- 优化DNS解析:若URL多为不同域名,DNS解析可能成为瓶颈,可搭配
aiodns库实现异步DNS解析。 - 控制连接池大小:
TCPConnector的limit参数不要超过系统能承受的文件描述符数量,否则会抛出"Too many open files"错误。 - 跳过响应内容:如果你的需求仅为发送请求而非获取响应,去掉
await response.text()能大幅提升速度。
内容的提问来源于stack exchange,提问作者Tyoslax Beffs




