You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

aiohttp(基于asyncio)批量请求效率低下问题求助

批量获取域名跳转URL的性能瓶颈排查与优化方案

嘿,我来帮你拆解下这个问题——从每秒100次直接掉到5秒1次,还远达不到教程里的10万次/分钟,大概率是你的脚本在并发控制、资源管理或者请求配置上踩了坑。咱们一步步来排查和优化:

一、先揪出最常见的坑:并发控制没做好

教程里的10万次/分钟是高并发优化后的极限性能,如果你的脚本是串行请求或者并发数没调好,肯定达不到预期,甚至越跑越慢:

  • 要是你用的是requests这种同步库串行发请求:每秒100次都算不错了,因为同步请求是阻塞的——必须等前一个请求拿到响应,才能发下一个,完全没利用好机器的网络能力;
  • 要是用了aiohttp这类异步库,但没控制并发量:一下子发起上万个请求,系统的文件句柄、网络连接池直接被占满,操作系统开始限制新连接,请求就会排队,越积越多,最后慢到离谱。

对应的优化方案:

用异步框架+信号量严格控制并发数,比如先从500-1000的并发量开始试,根据你的机器性能调整。给你个极简示例:

import aiohttp
import asyncio

async def get_final_url(session, domain):
    try:
        # 允许自动跳转,设置超时避免拖慢整体
        async with session.get(f"http://{domain}", allow_redirects=True, timeout=10) as resp:
            return (domain, str(resp.url))
    except Exception as e:
        return (domain, f"Error: {str(e)}")

async def main(domain_list):
    # 信号量控制并发数,这里设800,你可以根据机器情况增减
    semaphore = asyncio.Semaphore(800)
    
    # 复用一个ClientSession,关键!不然每次请求都新建连接
    async with aiohttp.ClientSession() as session:
        tasks = []
        for domain in domain_list:
            # 用信号量包装任务,限制并发
            async def wrapped_task(d):
                async with semaphore:
                    return await get_final_url(session, d)
            tasks.append(asyncio.create_task(wrapped_task(domain)))
        
        # 批量执行任务
        results = await asyncio.gather(*tasks)
        return results

if __name__ == "__main__":
    # 替换成你的域名列表
    domains = ["example.com", "test.com", "github.com"]
    final_results = asyncio.run(main(domains))
    for domain, url in final_results:
        print(f"{domain} -> {url}")

二、别忽略:HTTP连接池必须复用

每次请求都新建连接,会消耗大量的TCP握手、TLS握手时间,这也是导致脚本越跑越慢的关键:

  • 同步库requests要复用同一个requests.Session()实例,而不是每次请求都新建;
  • 异步库aiohttp.ClientSession本身就是复用连接池的,但要确保整个脚本只用一个Session,别在循环里反复创建。

三、警惕被限流:服务器会“拉黑”高频请求

短时间内发大量请求,很多域名的服务器会触发限流机制(返回429错误),甚至直接拒绝连接。如果你的脚本没处理这种情况,就会卡在等待超时上,整体速度暴跌:

优化点:

  • 给请求加合理的超时时间:比如10秒,避免一个慢请求拖垮整个任务队列;
  • 加指数退避重试:第一次失败等1秒,第二次等2秒,最多重试3次,别死磕一个失败的请求;
  • 加随机延迟:给每个请求加个几毫秒的随机延迟,模拟人类请求的随机性,降低被限流的概率。

四、检查系统资源是否被耗尽

你的机器CPU、内存、网络带宽可能撑不住高并发请求:

  • 看CPU使用率:如果跑满了,说明并发数太高,或者脚本有不必要的CPU密集型逻辑;
  • 看网络带宽:如果上传/下载带宽跑满了,请求自然会排队;
  • 调大文件句柄数:Linux下默认文件句柄数只有1024,高并发下不够用,可以用ulimit -n 65535临时调高(永久修改需要改系统配置)。

五、DNS解析可能拖后腿

批量请求大量不同域名时,DNS解析会成为隐形瓶颈——每次请求都要先解析域名到IP,解析慢了整个请求就慢了:

优化方案:

  • 用更快的DNS服务器:比如把系统DNS改成1.1.1.1或者8.8.8.8;
  • 提前批量解析所有域名的IP:然后直接请求IP,加上Host头,跳过每次请求的DNS解析步骤。示例代码片段:
import socket

# 提前批量解析域名
def pre_resolve_domains(domains):
    domain_ip_map = {}
    for domain in domains:
        try:
            ip = socket.gethostbyname(domain)
            domain_ip_map[domain] = ip
        except socket.gaierror:
            domain_ip_map[domain] = None
    return domain_ip_map

# 请求时直接用IP,加Host头
async def get_final_url_by_ip(session, domain, ip):
    if not ip:
        return (domain, "DNS解析失败")
    try:
        async with session.get(
            f"http://{ip}",
            headers={"Host": domain},
            allow_redirects=True,
            timeout=10
        ) as resp:
            return (domain, str(resp.url))
    except Exception as e:
        return (domain, f"Error: {str(e)}")

最后总结

先从并发控制+连接池复用入手,这两个是最容易快速提升性能的;然后排查是否被限流,调整重试和延迟策略;最后检查系统资源和DNS解析。一步步调整,应该能接近教程的性能水平。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:17:18