返回介绍

8.5 AsyncIO

发布于 2024-01-25 21:44:08 字数 2708 浏览 0 评论 0 收藏 0

作为对使用异步函数来处理重量级I/O系统的风潮的回应,Python 3.4+引入了对老式的asyncio标准库模块的改造。这个模块备受gevent和tornado并发方法的影响,定义了协程,并可以从协程切换,从而暂停当前函数的执行并允许让其他协程运行。就像在tornado中那样,事件循环显式地启动来开始执行协程。此外,Python 3引入了一个新的关键词——yield from,大大简化了对这些协程的处理(我们不再需要从一个协程中抛出异常来返回值,就如我们在例8-6中做的那样)。

值得重视的是asyncio库是很低层的,并不对用户提供更高层的功能。例如,尽管有很全面的socketAPI,但却没有简单的方法来做HTTP请求。作为结果,我们在例8-8中选择使用aiohttp。无论如何,对asyncio库的采用正开始增长,辅助模块的前景可能正在飞速变化。

例8-8 asyncio HTTP爬虫

import asyncio
import aiohttp
import random
import string

def generate_urls(base_url, num_urls):
  for i in range(num_urls):
    yield base_url + "".join(random.sample(string.ascii_lowercase, 10))

def chunked_http_client(num_chunks):
  semaphore = asyncio.Semaphore(num_chunks) # ❶
  @asyncio.coroutine
  def http_get(url): # ❷
    nonlocal semaphore
    with (yield from semaphore):
      response = yield from aiohttp.request('GET', url)
      body = yield from response.content.read()
      yield from response.wait_for_close()
    return body
  return http_get

def run_experiment(base_url, num_iter=500):
  urls = generate_urls(base_url, num_iter)
   http_client = chunked_http_client(100)
   tasks = [http_client(url) for url in urls] # ❸
   responses_sum = 0
   for future in asyncio.as_completed(tasks): # ❹
    data = yield from future
    responses_sum += len(data)
  return responses_sum

if __name__ == "__main__":
  import time
  delay = 100
  num_iter = 500
  base_url = "http://127.0.0.1:8080/add?name=asyncio&delay={}&".format(delay)
  loop = asyncio.get_event_loop()

  start = time.time()
  result = loop.run_until_complete(run_experiment(base_url, num_iter))
  end = time.time()
  print("{} {}".format(result, end-start))

❶ 正如在gevent中的例子一样,我们必须使用信号量来限制请求数量。

❷ 我们返回一个新的协程来异步下载文件,并遵从信号量的上锁。

❸ http_client函数返回futures。为了跟踪进度,我们把futures存入一个列表。

❹ 就像用gevent那样,我们能够等待futures准备就绪并去遍历它们。

asyncio模块的一个很大的好处就是与标准库相比熟悉的API,这样简化了创建辅助模块。我们能够得到与使用tornado或gevent相同类型的结果,但是如果我们想要,我们就能更深入地探索软件栈,并能用广泛的支持结构来创建我们自己的异步协议。此外,因为它是一个标准库模块,能够向我们确保这个模块总是遵照PEP并且得到了合理的维护[3]

而且,asyncio库允许我们统一像tornado和gevent这样的模块,让它们在相同的事件循环中运行。事实上,Python 3.4版本的tornado由asyncio库作为后台支持。结果就是,尽管tornado和gevent有着不同的使用场景,底层的事件循环是统一化的,这就让从一种模式切换到其他中间代码变得轻而易举。你甚至能基于asyncio模块之上相当容易地创建你自己的包装器,为了以对你正在解决的问题可能是最有效率的方式来与异步操作交互。

尽管它只在Python 3.4或更高的版本中[4]得到支持,这个模块至少是一个伟大的标志,将来有更多的工作将放入异步I/O中。因为Python开始在越来越多的处理流水线中占主导地位(从数据处理到web请求处理),这种转变意义深远。

图8-6显示了我们HTTP爬虫的asyncio版本的请求时间线。

图8-6 例8-8的HTTP请求时间表

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文