自学内容网 自学内容网

深入理解异步编程:使用 `asyncio` 和 `aiohttp` 进行并发请求

在现代的Web开发中,性能优化是一个非常重要的课题。特别是在处理大量网络请求时,如何高效地利用资源,减少等待时间,是每个开发者都需要面对的问题。Python 的 asyncio 库为我们提供了一种强大的工具,可以轻松实现异步编程,从而提高程序的并发性能。本文将通过一个具体的例子,详细讲解如何使用 asyncioaiohttp 进行并发网络请求。

1. 异步编程简介

异步编程是一种编程范式,它允许程序在等待某些操作(如网络请求、文件读写等)完成时,继续执行其他任务,而不是阻塞等待。Python 3.4 引入了 asyncio 库,使得异步编程变得更加简单和直观。

2. 代码结构概览

我们先来看一下代码的整体结构:

import asyncio
import aiohttp
from time import perf_counter

async def fetch(s, url):
    async with s.get(f'http://127.0.0.1:8000/items/{url}') as r:
        if r.status != 200:
            r.raise_for_status()
        return await r.text()

async def fetch_all(s, urls):
    tasks = []
    for url in urls:
        task = asyncio.create_task(fetch(s, url))
        tasks.append(task)
    res = await asyncio.gather(*tasks)
    return res

async def main():
    urls = range(1, 2500)
    async with aiohttp.ClientSession() as session:
        htmls = await fetch_all(session, urls)
        print(htmls)

if __name__ == '__main__':
    start = perf_counter()
    asyncio.run(main())
    stop = perf_counter()
    print("time taken:", stop - start)
3. 代码详解
3.1 fetch 函数

fetch 函数负责发送单个请求并返回响应内容。它使用了 aiohttp 库来发送异步 HTTP 请求。

async def fetch(s, url):
    async with s.get(f'http://127.0.0.1:8000/items/{url}') as r:
        if r.status != 200:
            r.raise_for_status()
        return await r.text()
  • async with s.get(...) as r: 使用 aiohttpClientSession 发送 GET 请求,并异步等待响应。
  • if r.status != 200: 检查响应状态码,如果不是 200,则抛出异常。
  • return await r.text(): 返回响应的文本内容。
3.2 fetch_all 函数

fetch_all 函数负责并发地发送多个请求,并将所有请求的结果收集起来。

async def fetch_all(s, urls):
    tasks = []
    for url in urls:
        task = asyncio.create_task(fetch(s, url))
        tasks.append(task)
    res = await asyncio.gather(*tasks)
    return res
  • tasks = []: 创建一个空列表来存储所有的任务。
  • for url in urls: 遍历所有的 URL,为每个 URL 创建一个异步任务。
  • task = asyncio.create_task(fetch(s, url)): 使用 asyncio.create_task 创建一个任务,并将其添加到任务列表中。
  • res = await asyncio.gather(*tasks): 使用 asyncio.gather 并发执行所有任务,并等待所有任务完成。
  • return res: 返回所有任务的结果。
3.3 main 函数

main 函数是整个程序的入口,它负责初始化 aiohttp.ClientSession,并调用 fetch_all 函数来获取所有 URL 的响应内容。

async def main():
    urls = range(1, 2500)
    async with aiohttp.ClientSession() as session:
        htmls = await fetch_all(session, urls)
        print(htmls)
  • urls = range(1, 2500): 创建一个包含 2499 个 URL 的列表。
  • async with aiohttp.ClientSession() as session: 创建一个 aiohttp.ClientSession 对象,用于发送 HTTP 请求。
  • htmls = await fetch_all(session, urls): 调用 fetch_all 函数,获取所有 URL 的响应内容。
  • print(htmls): 打印所有响应内容。
3.4 主程序

在主程序中,我们使用 asyncio.run 来运行 main 函数,并计算整个程序的执行时间。

if __name__ == '__main__':
    start = perf_counter()
    asyncio.run(main())
    stop = perf_counter()
    print("time taken:", stop - start)
  • start = perf_counter(): 记录程序开始执行的时间。
  • asyncio.run(main()): 运行 main 函数。
  • stop = perf_counter(): 记录程序结束执行的时间。
  • print("time taken:", stop - start): 打印程序的执行时间。
4. 性能分析

在代码的最后,我们打印了程序的执行时间。通过这种方式,我们可以直观地看到异步编程带来的性能提升。在我的测试环境中,执行时间大约为 1.348 秒,这对于 2499 个并发请求来说是非常高效的。

5. 总结

通过这个例子,我们可以看到 asyncioaiohttp 的强大之处。它们使得并发编程变得简单而高效,特别适合处理大量网络请求的场景。对于初级 Python 程序员来说,掌握异步编程是一个非常有价值的技能,它不仅能提高代码的性能,还能让你的程序更加现代化和高效。


原文地址:https://blog.csdn.net/engchina/article/details/144163557

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!