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

aiohttp性能问题:主线程执行连接导致请求速度过慢

分析你的异步代码性能问题

嘿,我来帮你拆解下这个问题!你提到异步实现比线程池版本慢,且URL越多耗时越长,还特意用不存在的服务器触发超时——这个测试场景太关键了,连接超时最容易暴露异步代码里的隐性阻塞点,咱们一步步捋:

最可能的核心问题:连接建立是同步阻塞的

很多人写异步代码时,容易踩一个坑:看起来用了async/await,但底层连接建立的逻辑还是同步的!比如:

  • 用了完全不支持异步的同步网络库(比如普通的requests);
  • 手动写socket时用了socket.connect()这种同步方法,而非异步的socket API(比如asyncio.open_connection());
  • 异步库默认用系统同步DNS解析,在超时场景下会串行等待每个域名解析完成。

这种情况下,你的“异步”代码本质是串行阻塞执行:每个URL都要等完超时时间(比如30秒)才会处理下一个,50个URL就是50×30秒的叠加耗时,自然比线程池的并行超时(总耗时接近单个超时时间)慢得多。

其次要检查:任务是否真的被并发调度了

就算用了正确的异步库,如果任务调度方式错了,也会变成串行执行。比如你可能写了这样的代码:

# ❌ 错误:逐个await,串行执行
for url in urls:
    await fetch_url(url)

而正确的并发写法应该是把所有任务放进asyncio.gather(),让事件循环同时调度:

# ✅ 正确:并发执行所有任务
tasks = [fetch_url(url) for url in urls]
await asyncio.gather(*tasks)

前者每个任务必须等上一个完成才启动,超时时间叠加;后者所有任务同时触发,总耗时只受最长的那个超时影响。

其他可能的坑

  • 异步库使用姿势错误:比如用aiohttp时每次请求都新建ClientSession,没有复用连接池;或者没设置正确的超时参数,导致底层逻辑异常阻塞。
  • 事件循环被其他同步操作阻塞:如果你的代码里还有CPU密集型计算、调用同步第三方库的逻辑,会占满事件循环的时间片,导致异步连接任务无法及时被调度,看起来就像连接没异步一样。

给你一个修正后的示例(用aiohttp)

import asyncio
import aiohttp

async def fetch(session, url):
    try:
        # 设置总超时,避免无限等待
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp:
            return f"{url} 响应:{resp.status}"
    except Exception as e:
        return f"{url} 错误:{str(e)}"

async def main(urls):
    # 复用ClientSession,避免重复创建连接开销
    async with aiohttp.ClientSession() as session:
        # 创建所有任务,并发执行
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for res in results:
            print(res)

if __name__ == "__main__":
    # 模拟50个不存在的URL
    urls = [f"http://nonexistent-server-{i}.com" for i in range(50)]
    asyncio.run(main(urls))

这个版本里,连接建立、DNS解析(如果额外配置aiodns会更优)都是异步的,且所有任务并发调度,总耗时会接近5秒(设置的超时时间),而不是50×5秒的叠加耗时。

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

火山引擎 最新活动