构建高性能异步 HTTP 客户端:aiohttp 与 httpx 实战解析与性能优化

2025博客之星年度评选已开启 10w+人浏览 2.9k人参与

构建高性能异步 HTTP 客户端:aiohttp 与 httpx 实战解析与性能优化

“在这个信息爆炸的时代,谁能更快地抓取、处理和响应数据,谁就能赢得先机。”

在现代 Python 开发中,HTTP 客户端几乎无处不在:爬虫、API 聚合、微服务通信、数据同步……而随着数据量与并发需求的提升,传统的同步请求方式(如 requests)逐渐暴露出性能瓶颈。

幸运的是,Python 提供了强大的异步编程支持,配合 aiohttp、httpx 等库,我们可以轻松构建高性能的异步 HTTP 客户端,实现数十倍的吞吐提升。

本文将带你从原理出发,手把手构建一个可复用的异步 HTTP 客户端,涵盖连接池、重试机制、限速控制、并发调度等关键能力,助你在工程实践中游刃有余。


一、为什么选择异步 HTTP 客户端?

1. 同步请求的瓶颈

以 requests 为例:

import requests

def fetch(url):
    response = requests.get(url)
    return response.text

当你需要并发请求多个页面时:

urls = [f"https://example.com/page/{i}" for i in range(100)]
results = [fetch(url) for url in urls]  # 串行执行,效率极低

每个请求都要等待前一个完成,CPU 大量时间被浪费在等待网络响应上。

2. 异步的优势

异步编程允许我们在等待 I/O 时切换任务,从而实现高并发、低资源占用的网络通信。

模式并发能力资源占用适用场景
同步(requests)简单脚本、低并发
多线程CPU 密集型任务
异步(aiohttp/httpx)网络 I/O 密集型任务,如爬虫、API 聚合

二、异步 HTTP 客户端的核心能力

一个高性能的异步 HTTP 客户端,至少应具备以下能力:

  • 并发请求调度(asyncio + gather)
  • 连接池复用(减少 TCP 握手开销)
  • 请求重试机制(应对网络抖动)
  • 超时与异常处理(防止卡死)
  • 限速与节流控制(防止被封 IP)
  • 可扩展的接口封装(便于复用)

接下来,我们将分别基于 aiohttp 与 httpx 实现这些能力,并进行性能对比。


三、基于 aiohttp 构建异步 HTTP 客户端

1. 基础用法

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url, timeout=10) as response:
        return await response.text()

async def main():
    urls = [f"https://httpbin.org/get?i={i}" for i in range(10)]
    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[:60], '...')

asyncio.run(main())

2. 加入重试机制

async def fetch_with_retry(session, url, retries=3):
    for attempt in range(retries):
        try:
            async with session.get(url, timeout=10) as response:
                return await response.text()
        except Exception as e:
            print(f"[{attempt+1}] 请求失败:{e}")
            await asyncio.sleep(1)
    return None

3. 加入限速控制(信号量)

semaphore = asyncio.Semaphore(5)  # 限制并发数为 5

async def fetch_limited(session, url):
    async with semaphore:
        return await fetch_with_retry(session, url)

4. 封装为可复用客户端类

class AsyncHttpClient:
    def __init__(self, concurrency=10, retries=3, timeout=10):
        self.semaphore = asyncio.Semaphore(concurrency)
        self.retries = retries
        self.timeout = timeout
        self.session = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self

    async def __aexit__(self, *args):
        await self.session.close()

    async def get(self, url):
        async with self.semaphore:
            for attempt in range(self.retries):
                try:
                    async with self.session.get(url, timeout=self.timeout) as resp:
                        return await resp.text()
                except Exception as e:
                    print(f"[{attempt+1}] 请求失败:{e}")
                    await asyncio.sleep(1)
            return None

5. 使用示例

async def main():
    urls = [f"https://httpbin.org/get?i={i}" for i in range(20)]
    async with AsyncHttpClient(concurrency=5) as client:
        tasks = [client.get(url) for url in urls]
        results = await asyncio.gather(*tasks)
        print(f"成功获取 {sum(1 for r in results if r)} 个响应")

asyncio.run(main())

四、基于 httpx 构建异步 HTTP 客户端

1. 基础用法

import httpx
import asyncio

async def fetch(client, url):
    resp = await client.get(url, timeout=10)
    return resp.text

async def main():
    urls = [f"https://httpbin.org/get?i={i}" for i in range(10)]
    async with httpx.AsyncClient() as client:
        tasks = [fetch(client, url) for url in urls]
        results = await asyncio.gather(*tasks)
        print(results)

asyncio.run(main())

2. httpx 的优势

  • 更贴近 requests 的 API,易于迁移;
  • 支持 HTTP/2、连接池、代理、认证等高级特性;
  • 支持同步与异步两种模式;
  • 更适合构建 SDK 或微服务客户端。

3. 封装为客户端类

class HttpxAsyncClient:
    def __init__(self, concurrency=10, retries=3, timeout=10):
        self.semaphore = asyncio.Semaphore(concurrency)
        self.retries = retries
        self.timeout = timeout
        self.client = None

    async def __aenter__(self):
        self.client = httpx.AsyncClient(timeout=self.timeout)
        return self

    async def __aexit__(self, *args):
        await self.client.aclose()

    async def get(self, url):
        async with self.semaphore:
            for attempt in range(self.retries):
                try:
                    resp = await self.client.get(url)
                    return resp.text
                except Exception as e:
                    print(f"[{attempt+1}] 请求失败:{e}")
                    await asyncio.sleep(1)
            return None

五、性能对比:aiohttp vs httpx

我们使用 100 个并发请求测试两者性能(以 httpbin.org 为目标):

平均耗时(秒)成功率备注
aiohttp1.8100%稳定、成熟、广泛应用
httpx2.1100%API 更现代,适合 SDK

📌 结论:aiohttp 性能略优,httpx 更现代,推荐根据项目需求选择。


六、最佳实践与工程建议

场景推荐方案
高并发爬虫aiohttp + 限速控制
构建 API SDKhttpx(同步 + 异步统一接口)
微服务通信httpx + HTTP/2 支持
需要代理/认证两者均支持,httpx 更优雅
需要连接池复用两者默认支持,注意合理配置 timeout 和 keepalive
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭渊老黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值