【问题标题】:Asyncio Making HTTP Requests Slower?Asyncio 让 HTTP 请求变慢?
【发布时间】:2015-04-07 15:43:03
【问题描述】:

我正在使用 Asyncio 和 Requests 对一系列 HTTP 请求进行基准测试。

出于某种原因,使用 Asyncio 比直接使用请求要慢一些。知道为什么吗?我是否错误地使用了 Asyncio?

import asyncio
import functools
import requests
import time

ts = time.time()
for i in range(10):
  @asyncio.coroutine
  def do_checks():
      loop = asyncio.get_event_loop()
      req = loop.run_in_executor(None, functools.partial(requests.get, "http://google.com", timeout=3))
      resp = yield from req
      print(resp.status_code)

  loop = asyncio.get_event_loop()
  loop.run_until_complete(do_checks())
te = time.time()
print("Version A: " + str(te - ts))

ts = time.time()
for i in range(10):
  r = requests.get("http://google.com", timeout=3)
  print(r.status_code)
te = time.time()

print("Version B:  " + str(te - ts))

输出:

版本 A = 异步;版本 B = 请求

200
200
200
200
200
200
200
200
200
200
Version A: 5.7215821743011475
200
200
200
200
200
200
200
200
200
200
Version B:  5.320340156555176

【问题讨论】:

    标签: python performance python-3.x benchmarking python-asyncio


    【解决方案1】:

    在开始下一个请求之前,您正在等待每个请求完成。所以你有事件循环的开销而没有任何好处。

    试试这个:

    import asyncio
    import functools
    import requests
    import time
    
    ts = time.time()
    loop = asyncio.get_event_loop()
    
    @asyncio.coroutine
    def do_checks():
        futures = []
        for i in range(10):
            futures.append(loop.run_in_executor(None, functools.partial(requests.get, "http://google.com", timeout=3)))
    
        for req in asyncio.as_completed(futures):
            resp = yield from req
            print(resp.status_code)
    
    loop.run_until_complete(do_checks())
    te = time.time()
    print("Version A: " + str(te - ts))
    
    ts = time.time()
    for i in range(10):
        r = requests.get("http://google.com", timeout=3)
        print(r.status_code)
    te = time.time()
    print("Version B:  " + str(te - ts))
    

    这是我运行它时得到的:

    $ python test.py 
    200
    ...
    Version A: 0.43438172340393066
    200
    ...
    Version B: 1.6541109085083008
    

    快得多,但实际上这只是产生线程并等待 http 库完成,您不需要 asyncio 来执行此操作。

    您可能需要查看aiohttp,因为它是为与asyncio 一起使用而构建的。 requests 是一个很棒的库,但它不是为 asyncio 制作的。

    【讨论】:

    • 很好的答案,有帮助!使用asyncio 生成线程而不是直接执行它有什么好处吗?
    • @okoboko 如果您要使用requests,则实际上没有必要使用asyncio,除非您的项目中有其他组件实际上是为与@987654331 一起使用而设计的@。如果是这种情况,你应该更喜欢aiohttp 而不是requests,除非你需要requests 的一个功能,而aiohttp 没有。
    • 带有loop.run_in_executor 的注释 - 当您使用默认执行程序时(通过将None 作为第一个参数传递),您使用的是具有五个线程的concurrent.futures.ThreadPoolExecutor。这意味着您只能同时运行五个请求,这对于 I/O 绑定的工作负载来说是相当低的。如果您使用更多线程创建自己的 ThreadPoolExecutor,您可能会获得更好的性能。
    【解决方案2】:

    为了完整起见,这里是一个非常快速的 asyncio 实现

    import aiohttp
    import asyncio
    import time
    
    async def main(n):
        ts = time.time()
        session = aiohttp.ClientSession()
        fs = [session.get('http://google.com') for _ in range(n)]
        for f in asyncio.as_completed(fs):
            resp = await f
            print(resp.status)
            await resp.release()
        await session.close()
        te = time.time()
        print("Aiohttp version:  " + str(te - ts))
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(10))
    loop.close()
    

    代码是python 3.5及更高版本。

    ~> python asyncioreq.py
    200
    ...
    Aiohttp version:  0.15974688529968262
    

    希望有人可以使用它;)

    【讨论】:

    • await session.close() 或将其用作上下文管理器。我收到一个错误,即 session.close() 从未等待。
    【解决方案3】:

    以@brunsgaard 的回答为基础:您可以使用 aiohttp 更进一步,并使用asyncio.gather() 收集您的结果。然后,您可以从您的请求中获取响应并进行处理。

    import aiohttp
    import asyncio
    import time
    
    async def main(n):
        start = time.time()
        session = aiohttp.ClientSession()
        jobs = [session.get('http://google.com') for _ in range(n)]
        done_jobs = await asyncio.gather(*jobs)
        for done_job in done_jobs:
            print(done_job.status)
        session.close()
        end = time.time()
        print("Time:  " + str(end - start))
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(10))
    loop.close()
    

    【讨论】:

    • 这是实现目标的更简洁的方法。应该是正确答案。
    猜你喜欢
    • 2018-04-28
    • 1970-01-01
    • 2018-06-28
    • 2020-01-17
    • 1970-01-01
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多