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

使用asyncio与aiohttp发送异步请求时部分请求无响应问题排查

问题原因及解决方案

核心原因分析

  1. 服务器静默限流
    很多服务器面对高频批量请求时,不会返回40X类状态码(比如429请求过多),而是直接在TCP层面丢弃连接或不发送任何响应。这种静默处理比返回明确状态码更隐蔽,能有效阻止自动化脚本的持续请求,你的未响应请求就是因为服务器没回包,而代码又没设置超时,导致任务一直挂起。

  2. 缺少请求超时配置
    你的代码里没有给session.get设置超时时间,当服务器不响应时,请求会无限期等待,永远不会主动终止,这是任务挂起的直接原因。

  3. 并发数超出连接池/服务器承受上限
    aiohttp的ClientSession默认连接池上限是100个并发连接,当你创建超过100个任务时,多余的任务会排队等待空闲连接。如果已有连接因为服务器静默丢弃而挂起,排队的任务会一直卡住,无法获取执行机会。

解决方案

1. 添加请求超时

给每个请求设置超时,避免无限等待:

import asyncio
import aiohttp

async def make_request(session, link, i):
    try:
        # 设置10秒总超时(包含连接和响应等待时间)
        async with session.get(link, timeout=aiohttp.ClientTimeout(total=10)) as response:
            loop = asyncio.get_running_loop()
            print(len(asyncio.tasks.all_tasks(loop)))
            print(f"request:{i}")
            print(response.status)
    except asyncio.TimeoutError:
        print(f"request:{i} 超时无响应")
    except aiohttp.ClientError as e:
        print(f"request:{i} 连接错误: {str(e)}")

async def make_task(link):
    tasks = []
    async with aiohttp.ClientSession() as session:
        tasks = [asyncio.create_task(make_request(session, link, i)) for i in range(no_of_requests)]
        await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)

asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(make_task("https://ocw.mit.edu"), debug=True)

2. 限制并发请求数

用asyncio.Semaphore控制同时发送的请求数,避免瞬间触发服务器限流:

import asyncio
import aiohttp

async def make_request(session, link, i, semaphore):
    async with semaphore:  # 控制并发数
        try:
            async with session.get(link, timeout=aiohttp.ClientTimeout(total=10)) as response:
                loop = asyncio.get_running_loop()
                print(len(asyncio.tasks.all_tasks(loop)))
                print(f"request:{i}")
                print(response.status)
        except asyncio.TimeoutError:
            print(f"request:{i} 超时无响应")
        except aiohttp.ClientError as e:
            print(f"request:{i} 连接错误: {str(e)}")

async def make_task(link):
    semaphore = asyncio.Semaphore(50)  # 限制最多50个并发请求
    tasks = []
    async with aiohttp.ClientSession() as session:
        tasks = [asyncio.create_task(make_request(session, link, i, semaphore)) for i in range(no_of_requests)]
        await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)

asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(make_task("https://ocw.mit.edu"), debug=True)

补充说明

服务器选择静默丢弃而非返回40X,本质是为了避免脚本通过识别状态码自动调整请求策略(比如自动降频、重试),这种策略在反爬机制中很常见。限制并发数不仅能降低触发限流的概率,还能减少本地资源占用,让程序运行更稳定。

内容的提问来源于stack exchange,提问作者Bilal Ahmed Karbelkar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:15:27