You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

高性能硬件环境下能否1秒内向10万个网站发送异步请求?线程池代码资源利用率低如何优化?

问题分析与解决方案

首先咱们先拆解你遇到的核心问题:

  • 你用的requests同步阻塞IO库,配合ThreadPoolExecutor本质是用线程模拟并发,并非真正的异步IO。当线程数拉到极高(比如10万URL就开5万线程),系统会把大量资源消耗在线程上下文切换上,这就是前1000次快、后续越来越慢的根本原因——线程爆炸导致的资源浪费。
  • 另外,requests默认HTTP连接池大小只有10,就算开了大量线程,大部分线程都在等待连接池释放连接,进一步放大了性能瓶颈。

能不能1秒内向100,000个网站发送异步请求?

理论上有实现可能,但需要满足几个关键前提

  1. 带宽足够:1Gbit/s换算为字节是约125MB/s,如果每个请求的HTTP头+内容平均为1KB,10万请求总大小是100MB,刚好在带宽承载范围内;若请求内容更大,带宽会成为瓶颈。
  2. 系统参数调优:默认的Linux/Windows系统对并发TCP连接、文件描述符的限制极低(比如Linux默认文件描述符仅1024),必须先调整这些参数才能支撑10万并发:
    • Linux下临时生效:ulimit -n 100000,永久生效需修改/etc/security/limits.conf
    • 调整TCP内核参数,比如开启tcp_tw_reuse、增大tcp_max_syn_backlog等,减少TIME_WAIT连接占用
  3. 目标服务器的响应能力:如果大部分目标网站服务器响应慢或拒绝高频请求,你的请求成功率会下降,但发送请求的速度仍能达标。

真正的异步解决方案:使用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} 秒")

额外优化建议

  1. 分批次处理:如果10万请求一次性发起,可能瞬间打满系统网络栈,可以把URL分成多批次(比如每批1万)依次处理,避免瞬间压力过大。
  2. 优化DNS解析:若URL多为不同域名,DNS解析可能成为瓶颈,可搭配aiodns库实现异步DNS解析。
  3. 控制连接池大小TCPConnectorlimit参数不要超过系统能承受的文件描述符数量,否则会抛出"Too many open files"错误。
  4. 跳过响应内容:如果你的需求仅为发送请求而非获取响应,去掉await response.text()能大幅提升速度。

内容的提问来源于stack exchange,提问作者Tyoslax Beffs

火山引擎 最新活动