3、HTTP请求库选型:requests vs httpx vs aiohttp

做Python后端开发,HTTP请求库几乎是每天都要打交道的工具。我见过不少团队,项目刚开始随便选一个库,结果到了要上异步、要调优性能的时候,才发现选错了,改起来那叫一个痛苦。今天我就把这三个主流库掰开揉碎讲清楚,帮你一次选对。

3.1 三巨头的基本画像

先给个直观的对比,你心里有个底:

特性 requests httpx aiohttp
同步支持 ✅ 原生 ✅ 原生 ❌ 需额外封装
异步支持 ❌ ✅ 原生 ✅ 原生
HTTP/2 ❌ ✅ ❌
连接池 ✅ urllib3 ✅ 内置 ✅ 内置
学习曲线 ⭐ 极低 ⭐⭐ 中等 ⭐⭐⭐ 较高

3.2 requests:老牌劲旅,但该退场了

说实话,requests 是我最早用的HTTP库。API设计确实优雅,一句 requests.get(url) 就能搞定。但为什么我现在不推荐在新项目里用它?

核心问题:它不支持异步。 你想想看,现在哪个后端项目不是高并发场景?requests 发一个请求,线程就卡在那等响应。如果同时发100个请求,就得开100个线程,资源开销大得吓人。

适用场景:

  • 简单的脚本、爬虫小工具
  • 对性能没要求的内部工具
  • 快速原型验证

避坑: 我曾经接手过一个项目,用 requests 做高并发的API网关,结果线上频繁超时。一查,连接池默认只有10个连接,大量请求在排队。后来换成 httpx,同样代码量,吞吐量翻了3倍。

3.3 httpx:现代同步异步双修选手

httpx 是我目前的主力库。它最大的优势是:同一套API,同步异步都能用。这意味着你可以在同步代码里先跑通逻辑,再无缝切换到异步模式。

看个例子就明白了:

# 同步模式
import httpx

with httpx.Client() as client:
    response = client.get('https://api.example.com/data')
    print(response.json())

# 异步模式
import httpx
import asyncio

async def fetch():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://api.example.com/data')
        print(response.json())

asyncio.run(fetch())

嗯,这里要注意:不要混用 Client 和 AsyncClient。我见过有人在一个协程里用了同步的 Client,结果整个事件循环被阻塞,性能反而比纯同步还差。

3.4 aiohttp:异步老将,但API不够友好

aiohttp 是 Python 异步生态的老牌库。性能确实强悍,但它的API设计……怎么说呢,有点「反人类」。比如获取响应内容,你得用 await resp.text() 或者 await resp.json(),而不是直接 resp.json()。这跟 httpx 比起来,写起来就啰嗦不少。

我的建议: 如果你团队里都是异步老手,项目对性能有极致要求(比如每秒上万请求),选 aiohttp。否则,优先考虑 httpx。

3.5 连接池配置:别让默认值坑了你

连接池说白了就是复用TCP连接,避免每次请求都重新握手。默认配置往往偏保守,你需要根据场景调整。

以 httpx 为例:

import httpx

# 自定义连接池
limits = httpx.Limits(
    max_keepalive_connections=20,   # 最大空闲连接数
    max_connections=100,            # 最大总连接数
    keepalive_expiry=30.0           # 空闲连接存活时间(秒)
)

client = httpx.Client(limits=limits)

为什么这么配? 我遇到过的情况是:默认的 max_connections=10,在并发稍高时,新请求就得排队等连接释放。把 max_keepalive_connections 设大一点,能减少频繁创建销毁连接的开销。

3.6 超时配置:三个维度都要设

超时配置是新手最容易忽略的。你以为设一个总超时就够了?其实要分三层:

  1. 连接超时:建立TCP连接的最大等待时间
  2. 读取超时:等待服务器返回数据的最大时间
  3. 写入超时:发送请求数据的最大时间

看 httpx 怎么配:

import httpx

# 精细控制
timeout = httpx.Timeout(
    connect=5.0,    # 连接超时5秒
    read=30.0,      # 读取超时30秒
    write=10.0,     # 写入超时10秒
    pool=3.0        # 连接池等待超时3秒
)

client = httpx.Client(timeout=timeout)

# 或者简单粗暴:统一设10秒
client = httpx.Client(timeout=10.0)

避坑: 我曾经在生产环境遇到过一个问题:某个第三方API偶尔响应慢,但没超时。结果所有请求都卡在读取阶段,连接池被占满,后续请求全部超时。后来把 read_timeout 从60秒降到15秒,配合重试机制,问题就解决了。

3.7 选型决策流程图

我画了个简单的决策流程,你照着走就行:

开始选型 需要异步? 性能极致? aiohttp httpx (异步) 简单脚本? requests httpx (同步)

3.8 总结:我的推荐

说了这么多,直接给结论:

  • 新项目默认选 httpx,同步异步都能用,API 现代,支持 HTTP/2
  • 老项目用 requests 的,别急着重构,除非你遇到了性能瓶颈
  • 追求极致异步性能,选 aiohttp,但要做好API不友好的心理准备
  • 连接池和超时一定要配,别用默认值,这是线上事故的高发区

我个人习惯是:写工具脚本用 requests,写服务用 httpx。简单直接,不折腾。