0

0

什么是aiohttp?它和requests有什么区别?

紅蓮之龍

紅蓮之龍

发布时间:2025-09-03 18:36:02

|

871人浏览过

|

来源于php中文网

原创

aiohttp基于asyncio实现异步非阻塞I/O,适合高并发场景;requests是同步阻塞库,简单易用。1. aiohttp适用于大量并发请求、构建异步Web服务及使用asyncio生态的项目;2. 其挑战包括学习曲线陡峭、调试复杂、需避免阻塞事件循环和资源管理要求高;3. 实际项目中可逐步迁移或按模块分离使用,异步为主时通过asyncio.to_thread调用同步代码以避免阻塞。

什么是aiohttp?它和requests有什么区别?

aiohttp
是一个基于
asyncio
的异步 HTTP 客户端/服务器框架,而
requests
则是一个简单、直观的同步 HTTP 客户端库。它们的核心区别在于处理并发的方式:
aiohttp
采用非阻塞 I/O 和事件循环,能高效处理大量并发连接;
requests
则是阻塞式的,一次处理一个请求。

解决方案

理解

aiohttp
requests
的不同,关键在于它们如何与 Python 的 I/O 操作打交道。

requests
库是许多 Python 开发者处理 HTTP 请求的首选,它的 API 设计得极其人性化,用起来简直是享受。当你需要向某个 API 发送一个 GET 请求,或者上传一些数据到服务器,
requests.get()
requests.post()
就能轻松搞定。它背后的机制是同步的:你发起一个请求,程序就会在那里等待,直到服务器响应或者超时。这对于大多数简单的任务来说非常方便,代码逻辑直观,一步一步执行,符合我们日常的思维模式。比如,你写个小脚本去抓取几个网页,或者给某个服务发个通知,
requests
简直是完美的选择。

import requests

try:
    response = requests.get("https://api.github.com/users/octocat", timeout=5)
    response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
    print("Requests 获取数据成功:", response.json())
except requests.exceptions.RequestException as e:
    print(f"Requests 请求失败: {e}")

然而,当你的应用需要同时处理成百上千个,甚至上万个网络请求时,

requests
的同步特性就会成为瓶颈。想象一下,如果你的程序要同时访问 1000 个不同的 URL,每个请求都得等待上几百毫秒甚至几秒,那么总耗时就会变得非常长。这时候,
aiohttp
就闪亮登场了。

aiohttp
基于 Python 的
asyncio
库,它提供了一种非阻塞的 I/O 模型。这意味着当你的程序发起一个网络请求时,它不会傻傻地原地等待响应,而是可以立即切换去处理其他任务(比如发起另一个请求),直到之前的请求有了结果再回来处理。这种“并发而非并行”的机制,使得
aiohttp
在处理大量 I/O 密集型任务时效率极高。它不仅仅是一个 HTTP 客户端,还能作为 HTTP 服务器来使用,构建高性能的 Web 应用。

import aiohttp
import asyncio

async def fetch_data(session, url):
    try:
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as response:
            response.raise_for_status() # Raises an aiohttp.ClientResponseError for bad responses
            return await response.json()
    except aiohttp.ClientError as e:
        print(f"Aiohttp 请求失败 ({url}): {e}")
        return None

async def main():
    urls = [
        "https://api.github.com/users/octocat",
        "https://api.github.com/users/torvalds",
        "https://api.github.com/users/guido",
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_data(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for i, result in enumerate(results):
            if result:
                print(f"Aiohttp 获取 {urls[i]} 数据成功: {result.get('login')}")
            else:
                print(f"Aiohttp 获取 {urls[i]} 数据失败。")

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

从上面的代码示例就能看出,

aiohttp
的代码结构会稍微复杂一些,因为它引入了
async/await
关键字和
asyncio
的事件循环概念。但这正是它实现高性能并发的代价。

什么时候应该优先选择aiohttp而不是requests?

选择

aiohttp
还是
requests
,从来都不是一个“哪个更好”的问题,而是一个“哪个更适合当前场景”的问题。我个人经验是,如果你遇到以下几种情况,那么
aiohttp
往往是更优解:

首先,当你需要处理大量并发的网络请求时,比如构建一个网络爬虫,需要同时抓取成千上万个页面数据;或者开发一个微服务,它需要向几十个甚至上百个外部 API 发送请求,并且希望响应时间尽可能短。在这种 I/O 密集型场景下,

aiohttp
的非阻塞特性能够显著提升程序的吞吐量和响应速度。
requests
在这种情况下,会因为频繁的等待而导致效率低下,程序大部分时间都花在“等待”上了。

其次,如果你正在构建一个高性能的 Web 服务器或者 WebSocket 服务。

aiohttp
不仅提供了异步客户端,它本身就是一个功能强大的异步 Web 框架,可以用来搭建 Web 服务,处理大量的并发连接,这正是
requests
所不具备的能力。比如,我曾用
aiohttp
搭建过一个需要实时处理大量客户端连接的后端服务,它的性能表现确实令人印象深刻。

最后,当你的项目已经在使用

asyncio
生态系统中的其他库时,比如
asyncpg
(异步 PostgreSQL 驱动)或
aioredis
(异步 Redis 客户端),那么继续使用
aiohttp
会让整个项目的技术栈保持一致性,代码风格也更统一,减少了在同步和异步上下文之间切换的认知负担。这种情况下,将
requests
引入一个纯异步项目,反而会显得格格不入,甚至可能带来一些不必要的阻塞问题。

aiohttp的异步编程模式有哪些挑战?

虽然

aiohttp
在性能上表现卓越,但它的异步编程模式确实带来了一些独特的挑战,对于习惯了同步编程的开发者来说,这需要一个适应过程。

DaGaoPeng(大高朋网团购程序)
DaGaoPeng(大高朋网团购程序)

大高朋团购系统是一套Groupon模式的开源团购程序,开发的一套网团购程序,系统采用ASP+ACCESS开发的团购程序,安装超简,功能超全面,在保留大高朋团购系统版权的前提下,允许所有用户免费使用。大高朋团购系统内置多种主流在线支付接口,所有网银用户均可无障碍支付;短信发送团购券和实物团购快递发货等。 二、为什么选择大高朋团购程序系统? 1.功能强大、细节完善 除了拥有主流团购网站功能,更特别支

下载

最明显的挑战就是学习曲线。你需要理解

async/await
关键字的含义、
asyncio
事件循环的工作原理、协程 (coroutines) 的概念以及它们如何调度。这不像
requests
那样,直接调用一个函数就能得到结果。你得思考什么时候该
await
,什么时候该创建
Task
,以及如何正确地管理并发任务。我记得刚开始接触
asyncio
时,对于
asyncio.gather
asyncio.create_task
的使用场景就纠结了很久,什么时候用哪个,以及它们的区别在哪里,需要花时间去消化。

其次是调试的复杂性。异步代码的执行流程不再是简单的线性顺序,它会在不同的协程之间跳转,这使得使用传统调试工具追踪代码执行路径变得更加困难。堆栈跟踪也可能因为协程的切换而变得冗长和难以理解,定位问题时需要更多的耐心和技巧。

再者,避免阻塞事件循环是使用

aiohttp
的一个核心原则,也是一个常见的陷阱。如果在一个
async
函数中不小心执行了耗时的同步操作(比如一个复杂的计算或者一个阻塞的数据库查询),那么整个事件循环就会被阻塞,导致所有其他正在等待的任务都无法执行,这会彻底破坏异步的优势。虽然
asyncio
提供了
loop.run_in_executor
来将阻塞操作放到单独的线程或进程池中执行,但这本身又增加了代码的复杂性。

最后,资源管理在异步环境中也需要特别注意。比如

aiohttp.ClientSession
这样的对象,它们需要被正确地关闭以释放底层连接。通常我们会使用
async with
语句来确保资源被妥善管理,但如果忘记或者处理不当,可能会导致资源泄漏。

如何在实际项目中有效地结合使用aiohttp和requests?

老实说,在同一个核心业务逻辑中,我通常会尽量避免同时混用

aiohttp
requests
。这主要是为了保持代码风格的一致性,减少认知负担,并且防止无意中引入阻塞问题。如果你正在构建一个异步应用,那么就尽可能地使用异步库;如果是一个同步应用,
requests
依然是你的好伙伴。

然而,在一些特定的场景下,你可能会发现它们各自在项目中的定位:

一种情况是逐步迁移。如果你有一个庞大的、基于

requests
的同步应用,但现在需要引入高性能的异步功能,比如一个新的实时数据处理模块。那么,你可以让新模块使用
aiohttp
,而旧的、稳定的同步部分继续使用
requests
。这是一种平滑过渡的方式,避免了一次性重构所有代码的巨大风险。

另一种情况是职责分离。在大型项目中,不同的服务或模块可能有着不同的性能和并发需求。例如,你的核心 API 网关可能使用

aiohttp
来处理高并发的外部请求,因为它需要快速响应;而一个后台数据清洗脚本,它可能只需要定时从某个外部源拉取少量数据,这时候用
requests
编写会更简单、更快捷,因为并发并不是它的主要考量。

还有一种情况,虽然不推荐作为常规做法,但有时你可能需要在一个异步

aiohttp
应用中调用一个只提供同步 API 的第三方库,而这个库内部又使用了
requests
。在这种情况下,如果你必须调用这个同步函数,你应该使用
asyncio.to_thread()
(Python 3.9+)或
loop.run_in_executor()
将其放到单独的线程中执行,以避免阻塞
aiohttp
的事件循环。

import asyncio
import requests
import aiohttp

# 模拟一个只提供同步API的函数,内部使用requests
def blocking_sync_fetch(url):
    print(f"在单独线程中执行同步请求: {url}")
    try:
        response = requests.get(url, timeout=3)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"同步请求失败: {e}")
        return None

async def main_async_task():
    async with aiohttp.ClientSession() as session:
        # 使用 aiohttp 发起异步请求
        async with session.get("https://api.github.com/users/octocat") as resp:
            data = await resp.json()
            print(f"Aiohttp 异步获取数据: {data.get('login')}")

        # 在异步上下文中调用同步函数,使用 asyncio.to_thread 防止阻塞
        print("\n--- 尝试在异步上下文中调用同步函数 ---")
        sync_result = await asyncio.to_thread(blocking_sync_fetch, "https://api.github.com/users/torvalds")
        if sync_result:
            print(f"通过 asyncio.to_thread 成功获取同步数据: {sync_result.get('login')}")

if __name__ == "__main__":
    asyncio.run(main_async_task())

这段代码展示了在一个

aiohttp
驱动的异步程序中,如何“安全地”调用一个依赖
requests
的同步函数。但再次强调,这通常是作为一种权宜之计,最佳实践是尽量保持技术栈的统一。如果可以,我会优先寻找该同步库的异步替代品,或者自己封装一个异步接口。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

716

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

627

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

743

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

617

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1236

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

575

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

699

2023.08.11

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

65

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号