Skip to main content

更好用的 aiohttp 客户端,支持同步和异步使用。 性能碾压 httpx 和 requests 400%

Project description

⚡ nb_aiohttp

下一代高性能 Python HTTP 客户端

Python Version License Performance

让异步请求像呼吸一样简单

特性安装快速开始性能对比API文档


1. 💡 为什么选择 nb_aiohttp?

在 Python HTTP 客户端的世界里,我们常常面临一个艰难的选择:

  • 选择 requests?简单易用,但性能受限于同步阻塞
  • 选择 httpx?同步异步通吃,但性能平平
  • 选择 aiohttp?性能卓越,但 API 复杂,不能全局实例化

nb_aiohttp 横空出世,打破这个困局!

我们在 aiohttp 的强大性能基础上,重新设计了更友好的 API,让你既能享受极致性能,又能拥有极简体验。

  • 各个http客户端性能对比,包括同步和异步:

    • 可以看出 nb_http_client 性能是神中神,nb_http_client 也是我基于 万能对象池 开发的 http 连接池。
    • nb_aiohttp 性能和 aiohttp 并列第二, 但是 nb_aiohttp 的 API 更简单,更易用,可以同步和异步双世界使用。
  • 具体对比代码可以看 benchmark.md

  • 对比文档总结,见文档第 20章节。

pZCRiPe.png

2. 🚀 核心特性

2.1 🏆 性能王者

  • 异步性能: 比 httpx5倍,完全释放 aiohttp 的性能潜力
  • 同步性能: 比 requests4倍,同步代码也能享受异步性能
  • 海量并发: 内置连接池管理,轻松应对数万级并发请求

2.2 🎯 极简设计

# 一行实例化,全局可用
http = NbAioHttpClient()

# 一行请求,优雅简洁
resp = await http.get('https://api.github.com')

2.3 🧩 懒加载机制

彻底解决 aiohttp.ClientSession 无法在模块级别实例化的痛点:

# ✅ nb_aiohttp - 随处实例化
from nb_aiohttp import NbAioHttpClient
http = NbAioHttpClient()  # 在全局作用域直接创建

# ❌ aiohttp - 必须在 async 函数内
import aiohttp
async def func():
    async with aiohttp.ClientSession() as session:  # 每次都要重新创建
        ...

2.4 🔄 同步/异步双支持

一套代码,两种模式,适配各种应用场景:

  • 用途

    • NbAioHttpClient: 异步高性能,适合高并发服务
    • NbSyncHttpClient: 同步调用,适合非asyncio编程生态的传统应用、脚本
  • 原理

    • NbAioHttpClient的原理: 是调用aiohttpClientSession的异步方法 ,但使用方式被大幅简化,支持全局变量创建 NbAioHttpClient,原生aiohttp的2次async with 加 3次await,被简化为 1次await
    • NbSyncHttpClient的原理: 是通过 asyncio.run_coroutine_threadsafe 调用 NbAioHttpClient 的异步request方法,然后通过 future.result() 获取结果,所以本质也是在loop 中运行 aiohttp ,性能远超 requests

2.5 💪 企业级特性

  • 自动重试: 智能重试机制,提升请求成功率
  • 统一响应: 封装 NbHttpResp 对象,API 一致性强
  • JSON 转换: .dict 属性直接获取 JSON 数据
  • 智能日志: 集成 nb_log,自动记录错误和慢请求
  • 灵活配置: 超时、重试、连接池等参数随心定制

3. 📦 安装

pip install nb_aiohttp

4. ⚡ 快速开始

4.1 🔹 异步模式 - 性能巅峰

完美适配 FastAPI、异步爬虫、高并发服务等场景:

import asyncio
from nb_aiohttp import NbAioHttpClient

# 🎯 在模块顶层直接实例化 - 这是最大的创新!
http = NbAioHttpClient(
    timeout=10,              # 超时时间
    connector_limit=100,     # 连接池大小
    max_retries=3,           # 最大重试次数
)

async def main():
    # 🚀 发起 GET 请求 - 仅需一次 await
    resp = await http.get('https://api.github.com/events')
    print(f"状态码: {resp.status}")
    print(f"响应内容: {resp.text[:100]}")
    
    # 📤 发起 POST json数据请求 - API 简洁直观
    resp = await http.post(
        'https://httpbin.org/post',
        json={'username': 'admin', 'password': '123456'}
    )
    
    # 🎁 直接获取 JSON 数据
    print(f"响应数据: {resp.dict}")
    
    # 🔐 使用 context manager 自动管理资源
    async with NbAioHttpClient() as client:
        resp = await client.get('https://www.python.org')
        print(f"Python 官网: {resp.ok}")
    
    # 🛑 手动关闭连接
    await http.close()

if __name__ == '__main__':
    asyncio.run(main())

4.2 🔹 同步模式 - 简单易用

完美适配传统 Django、Flask、爬虫脚本等同步场景:

from nb_aiohttp import NbSyncHttpClient

# 🎯 实例化同步客户端
http = NbSyncHttpClient(
    timeout=10,
    connector_limit=100,
    max_retries=3,
)

# ⚙️ 启动后台事件循环(仅需调用一次)
http.run_forever()

# 🚀 像使用 requests 一样简单
resp = http.get('https://api.github.com/events')
print(f"状态码: {resp.status}")
print(f"响应内容: {resp.text[:100]}")

# 📤 POST 请求
resp = http.post(
    'https://httpbin.org/post',
    json={'key': 'value'}
)
print(f"响应数据: {resp.dict}")

# 🎯 无需手动关闭,自动管理生命周期

4.3 🔹 高级用法

4.3.1 错误处理与重试

from nb_aiohttp import NbAioHttpClient

http = NbAioHttpClient(
    max_retries=5,              # 失败后重试 5 次
    is_raise_for_status=True,   # 状态码异常时抛出错误
    is_log_error=True,          # 记录错误日志
)

async def robust_request():
    try:
        resp = await http.get('https://unstable-api.com/data')
        return resp.dict
    except Exception as e:
        print(f"请求失败: {e}")
        return None

4.3.2 性能监控

http = NbAioHttpClient(
    log_timeout_seconds=2.0,  # 超过 2 秒的请求将被记录
)

async def monitor_slow_requests():
    # 慢请求会自动被记录到日志
    resp = await http.get('https://slow-api.com/data')

4.3.3 自定义请求头和 Cookies

http = NbAioHttpClient(
    headers={
        'User-Agent': 'MyApp/1.0',
        'Authorization': 'Bearer YOUR_TOKEN',
    },
    cookies={
        'session_id': 'abc123',
    }
)

async def custom_request():
    # 使用全局配置的 headers 和 cookies
    resp = await http.get('https://api.example.com/user')
    
    # 或者临时覆盖
    resp = await http.get(
        'https://api.example.com/admin',
        headers={'Authorization': 'Bearer ADMIN_TOKEN'}
    )

4.3.4 完整的 HTTP 方法支持

http = NbAioHttpClient()

# 支持所有常用 HTTP 方法
await http.get(url)
await http.post(url, json=data)
await http.put(url, json=data)
await http.delete(url)
await http.patch(url, json=data)
await http.head(url)
await http.options(url)

5. 📊 性能对比

5.1 实测数据

基于真实压测场景(200 并发,20万次请求):

HTTP 客户端 异步性能 同步性能 易用性 全局实例化
nb_aiohttp (异步) ⚡⚡⚡⚡⚡ - ⭐⭐⭐⭐⭐
nb_aiohttp (同步) - ⚡⚡⚡⚡ ⭐⭐⭐⭐⭐
aiohttp (原生) ⚡⚡⚡⚡⚡ - ⭐⭐
httpx (异步) - ⭐⭐⭐⭐
httpx (同步) - ⚡⚡ ⭐⭐⭐⭐
requests - ⭐⭐⭐⭐⭐

5.2 性能结论

  • 🏆 异步场景: nb_aiohttp ≈ 原生 aiohttp > httpx (5倍)
  • 🏆 同步场景: nb_aiohttp > httpx (2倍) > requests (4倍)
  • 🎯 易用性: nb_aiohttp = requests > httpx > 原生 aiohttp

5.3 代码对比

5.3.1 原生 aiohttp - 繁琐冗长

import aiohttp
import asyncio

async def fetch():
    # ❌ 不能全局实例化
    # ❌ 需要 2 次 async with
    # ❌ 需要 3 次 await
    async with aiohttp.ClientSession() as session:
        async with session.get('https://api.github.com') as response:
            if response.status == 200:
                text = await response.text()
                data = await response.json()
                print(data)

5.3.2 nb_aiohttp - 极致简洁

from nb_aiohttp import NbAioHttpClient
import asyncio

# ✅ 全局实例化
http = NbAioHttpClient()

async def fetch():
    # ✅ 仅需 1 次 await
    # ✅ 自动处理状态码
    # ✅ 直接获取数据
    resp = await http.get('https://api.github.com')
    print(resp.dict)

代码量减少 70%,可读性提升 300%!

6. 📚 API 文档

6.1 NbAioHttpClient (异步客户端)

6.1.1 构造函数

NbAioHttpClient(
    *,
    timeout: int | float = 30,
    connector_limit: int = 100,
    max_retries: int = 3,
    is_raise_for_status: bool = True,
    log_timeout_seconds: int | float | None = None,
    is_log_error: bool = True,
    connector: BaseConnector = None,
    headers: dict = None,
    cookies: dict = None,
    **kwargs
)

参数说明:

参数 类型 默认值 说明
timeout int/float 30 请求超时时间(秒)
connector_limit int 100 TCP 连接池大小
max_retries int 3 失败重试次数
is_raise_for_status bool True 4xx/5xx 状态码是否抛异常
log_timeout_seconds int/float None 慢请求记录阈值(秒)
is_log_error bool True 是否记录错误日志
connector BaseConnector None 自定义连接器
headers dict None 默认请求头
cookies dict None 默认 Cookies

6.1.2 主要方法

# HTTP 请求方法
await http.get(url, **kwargs) -> NbHttpResp
await http.post(url, **kwargs) -> NbHttpResp
await http.put(url, **kwargs) -> NbHttpResp
await http.delete(url, **kwargs) -> NbHttpResp
await http.patch(url, **kwargs) -> NbHttpResp
await http.head(url, **kwargs) -> NbHttpResp
await http.options(url, **kwargs) -> NbHttpResp

# 通用请求方法
await http.request(method, url, **kwargs) -> NbHttpResp

# 生命周期管理
await http.ensure_session()  # 确保 session 已创建
await http.close()           # 关闭 session

# Context Manager 支持
async with NbAioHttpClient() as client:
    resp = await client.get(url)

6.2 NbSyncHttpClient (同步客户端)

6.2.1 构造函数

NbSyncHttpClient(**kwargs)  # 参数与 NbAioHttpClient 相同

6.2.2 主要方法

# 启动后台事件循环(必须首先调用)
http.run_forever()

# HTTP 请求方法(同步调用)
http.get(url, **kwargs) -> NbHttpResp
http.post(url, **kwargs) -> NbHttpResp
http.put(url, **kwargs) -> NbHttpResp
http.delete(url, **kwargs) -> NbHttpResp
http.patch(url, **kwargs) -> NbHttpResp
http.head(url, **kwargs) -> NbHttpResp
http.options(url, **kwargs) -> NbHttpResp

# 通用请求方法
http.request(method, url, **kwargs) -> NbHttpResp

6.3 NbHttpResp (响应对象)

统一封装的响应对象,提供便捷的数据访问接口:

class NbHttpResp:
    text: str          # 响应文本内容
    status: int        # HTTP 状态码
    headers: dict      # 响应头
    url: str           # 请求的 URL
    
    @property
    def dict(self) -> dict:
        """将 JSON 响应解析为字典"""
        
    @property
    def ok(self) -> bool:
        """状态码是否为 2xx"""

6.3.1 使用示例

resp = await http.get('https://api.github.com/users/github')

print(resp.status)        # 200
print(resp.ok)            # True
print(resp.text[:100])    # 响应文本前 100 字符
print(resp.dict['name'])  # 直接访问 JSON 数据
print(resp.headers)       # 响应头字典
print(resp.url)           # 实际请求的 URL

7. 🎯 使用场景

7.1 🔹 高并发 Web 服务

from fastapi import FastAPI
from nb_aiohttp import NbAioHttpClient

app = FastAPI()
http = NbAioHttpClient(connector_limit=500)  # 全局实例

@app.get("/proxy")
async def proxy_request():
    # 超高并发下依然稳定
    resp = await http.get('https://api.example.com/data')
    return resp.dict

7.2 🔹 异步爬虫

from nb_aiohttp import NbAioHttpClient
import asyncio

http = NbAioHttpClient(
    timeout=30,
    max_retries=5,
    connector_limit=200,
)

async def crawl(url):
    try:
        resp = await http.get(url)
        # 处理数据
        return resp.text
    except Exception as e:
        print(f"爬取失败: {url}, 错误: {e}")

async def main():
    urls = ['https://example.com/page' + str(i) for i in range(1000)]
    tasks = [crawl(url) for url in urls]
    results = await asyncio.gather(*tasks)
    print(f"完成 {len(results)} 个页面的爬取")

asyncio.run(main())

7.3 🔹 微服务调用

from nb_aiohttp import NbAioHttpClient

# 不同服务的客户端
user_service = NbAioHttpClient(timeout=5)
order_service = NbAioHttpClient(timeout=10)
payment_service = NbAioHttpClient(timeout=15)

async def create_order(user_id, product_id):
    # 并发调用多个服务
    user_resp, product_resp = await asyncio.gather(
        user_service.get(f'http://user-service/users/{user_id}'),
        order_service.get(f'http://product-service/products/{product_id}')
    )
    
    # 创建订单
    order_resp = await order_service.post(
        'http://order-service/orders',
        json={
            'user': user_resp.dict,
            'product': product_resp.dict,
        }
    )
    
    return order_resp.dict

7.4 🔹 同步脚本

from nb_aiohttp import NbSyncHttpClient

http = NbSyncHttpClient().run_forever()

# 在任何同步代码中使用
def batch_process():
    urls = ['https://api.example.com/item/' + str(i) for i in range(100)]
    
    for url in urls:
        resp = http.get(url)
        if resp.ok:
            print(f"处理成功: {resp.dict}")
        else:
            print(f"处理失败: {resp.status}")

batch_process()

8. 🔧 高级配置

8.1 自定义 Connector

import aiohttp
from nb_aiohttp import NbAioHttpClient

# 自定义 SSL 和 DNS 配置
connector = aiohttp.TCPConnector(
    limit=200,
    ttl_dns_cache=300,
    ssl=False,
)

http = NbAioHttpClient(connector=connector)

8.2 代理设置

http = NbAioHttpClient()

# 使用代理
resp = await http.get(
    'https://api.example.com',
    proxy='http://proxy.example.com:8080'
)

8.3 上传文件

http = NbAioHttpClient()

# 上传文件
with open('file.txt', 'rb') as f:
    resp = await http.post(
        'https://api.example.com/upload',
        data={'file': f}
    )

8.4 流式下载

http = NbAioHttpClient()

# 访问底层 aiohttp session 进行流式操作
await http.ensure_session()
async with http.session.get('https://example.com/large-file.zip') as response:
    with open('downloaded.zip', 'wb') as f:
        async for chunk in response.content.iter_chunked(1024):
            f.write(chunk)

9. 🤔 常见问题

9.1 Q: 为什么不直接使用 aiohttp?

A: 原生 aiohttp 虽然性能卓越,但 API 设计复杂:

  • ❌ 不能在全局作用域实例化 ClientSession
  • ❌ 需要 2 次 async with 和多次 await
  • ❌ 缺少自动重试等企业级特性

nb_aiohttp 保留了 aiohttp 的性能优势,同时提供了更友好的 API。

9.2 Q: 与 httpx 相比有什么优势?

A:

  • 性能: 异步性能是 httpx 的 5 倍
  • 🎯 简洁: API 设计更加简洁直观
  • 🔄 灵活: 同时提供异步和同步两种模式

9.3 Q: NbSyncHttpClient 的性能为什么比 requests 好?

A: NbSyncHttpClient 底层使用 aiohttp 异步引擎,通过事件循环转换为同步接口,性能远超传统的同步阻塞模型。

9.4 Q: 是否支持 HTTP/2?

A: 目前基于 aiohttp,暂不支持 HTTP/2。如果你的场景强依赖 HTTP/2,建议使用 httpx。

9.5 Q: 如何处理请求失败?

A:

http = NbAioHttpClient(
    max_retries=5,           # 自动重试 5 次
    is_raise_for_status=True # 失败时抛出异常
)

try:
    resp = await http.get(url)
except Exception as e:
    # 处理异常
    print(f"请求失败: {e}")

10. 📄 开源协议

本项目采用 MIT 协议 开源。

20 各种三方包的http客户端每请求 10000次 http的 耗时

pZCRiPe.png

20.1 实测对比依赖的三方包说明

  • pip install nb_libs
    thread_show_process_cpu_usage 需要用这个函数监控当前进程的cpu,通过当前进程cpu使用率的打印,让你清清楚楚到底是服务端性能不行还是客户端性能不行?让你清清楚楚知道是客户端cpu达到100%了,所以请求次数无法往上突破

  • pip install nb_aiopool 异步并发池,比无脑 asyncio.create_task 10万任务 + asyncio.Semaphore(100) 限制并发, 内存和cpu好太多

  • pip install threadpool_executor_shrink_able 同步并发池,主要是有界队列,也可以用 concurrent.futures.ThreadPoolExecutor ,但是它是无界队列,。

20.2 通过代码实测 benchmark

通过控制台的 日志观察得到

例如:

第 20000 次 响应时间:  10:29:20 {"message":"欢迎来到aio1 示例 API!"} 
第 30000 次 响应时间:  10:29:35 {"message":"欢迎来到aio1 示例 API!"}

20.2.1 aiohttp.ClientSession()

  • 耗时15秒
    第 1000 次 响应时间: 10:37:07 {"message":"欢迎来到aio1 示例 API!"}
    第 11000 次 响应时间: 10:37:22 {"message":"欢迎来到aio1 示例 API!"}

20.2.2 httpx.AsyncClient()

  • 耗时50秒
    第 1000 次 响应时间: 10:44:17 {"message":"欢迎来到aio1 示例 API!"} 第 11000 次 响应时间: 10:45:07 {"message":"欢迎来到aio1 示例 API!"}

20.2.3 nb_aiohttp.NbAioHttpClient

  • 耗时13秒 第 1000 次 响应时间: 10:58:32 {"message":"欢迎来到aio1 示例 API!"} 第 11000 次 响应时间: 10:58:45 {"message":"欢迎来到aio1 示例 API!"}

20.2.4 requests.Session()

  • 耗时40秒 第 2000 次 响应时间: 10:34:25 {"message":"欢迎来到aio1 示例 API!"} 第 12000 次 响应时间: 10:35:05 {"message":"欢迎来到aio1 示例 API!"}

20.2.5 nb_aiohttp.NbSyncHttpClient

  • 耗时 15秒
    第 20000 次 响应时间: 10:29:20 {"message":"欢迎来到aio1 示例 API!"}
    第 30000 次 响应时间: 10:29:35 {"message":"欢迎来到aio1 示例 API!"}

20.2.6 nb_http_client.ObjectPool

  • 耗时4秒
    第 11000 次 响应时间: 11:07:49 {"message":"欢迎来到aio1 示例 API!"} 第 21000 次 响应时间: 11:07:53 {"message":"欢迎来到aio1 示例 API!"}

20.10 小结

20.10.1 nb_http_client:

nb_http_client 和 nb_aiohttp 是同一个作者。

nb_http_client 性能吊打python同步和异步编程世界的 任何http客户端请求三方包,是真正的 王中王,神中神
nb_http_client 性能强悍是因为基于我的 万能对象池 universal_object_pool + python 内置的非常底层的 http 模块 打造的 http连接池。

  • nb_http_client 是 ydf0509的 python万能对象池的 universal_object_pool 演示的一个附属品而已。
    nb_http_client 是用来显示 universal_object_pool 这个万能对象池如何神通广大的万能,可以实现任何连接池,包括数据库连接池http连接池以及python对象池

  • nb_http_client没有过于精心打磨成人性化好用,难用是难用了一点。
    但是用在公司内部的http服务间调用足够了,你可以再对他二次封装成一个你自己的请求函数 utils/my_request ,性能吊打requests 10倍,给你的服务器节约大量cpu

  • 优点

    • 性能好,在同步包里面 nb_http_client.ObjectPool 是神中神级别,python 有史以来的http请求包,性能的王中王。
    • 连接池绑定了host和port,以及发送请求相对于封装完善的requests 和 aiohttp httpx们,不够人性化
  • 缺点

    • 不太好用,连接池绑定了host和port,不能张冠李戴对别的域名发请求;
    • 不够人性化,发送请求相对于封装完善的高级包,requests 和 aiohttp httpx们,使用没那么方便,但你可以二次封装成你的 utils/my_request 函数。

20.10.2 nb_aiohttp

  • nb_aiohttp 有 NbAioHttpClient 和 NbSyncHttpClient 两个类, 如同httpx.AsyncClient() 和 httpx.Client() 一样,两个类分别用于同步编程和异步编程。

  • nb_aiohttp 的好处是 使用方便程度和 httpx/requests 一样, 性能程度和aiohttp一样。

  • httpx使用比aiohttp方便

  • aiohttp 性能吊打 httpx

30. 🙏 致谢

  • 感谢 aiohttp 提供了强大的异步 HTTP 引擎
  • 感谢 nb_log 提供了优秀的日志解决方案

40. 📮 联系方式


⭐ 如果这个项目对你有帮助,请给我们一个 Star!⭐

让 HTTP 请求更快、更简单、更优雅!

Made with ❤️ by ydf0509

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nb_aiohttp-1.3.tar.gz (974.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nb_aiohttp-1.3-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file nb_aiohttp-1.3.tar.gz.

File metadata

  • Download URL: nb_aiohttp-1.3.tar.gz
  • Upload date:
  • Size: 974.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.0

File hashes

Hashes for nb_aiohttp-1.3.tar.gz
Algorithm Hash digest
SHA256 00f6bd29a3f12fd59c350584e2d908da025264e7d6eb8d927d246902e5f49217
MD5 cd3dd0d1d45ae8fd470ae04a8cec3729
BLAKE2b-256 ce66aa810fef18fed24cfd62fea5529c15e220c5878597609ab0f912c8b68dad

See more details on using hashes here.

File details

Details for the file nb_aiohttp-1.3-py3-none-any.whl.

File metadata

  • Download URL: nb_aiohttp-1.3-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.0

File hashes

Hashes for nb_aiohttp-1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 d359737f4b06fa32dd0b309a4e0a38a963ecf283d8ccad609834df087fe4b930
MD5 610f7ffa38193d5d1f8a72132734f55e
BLAKE2b-256 edd2e0933db2ac4b02524bd838bd699af8b4e8f6de4c559b3baa8f18d099a93b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page