Asyncio使HTTP请求更慢?

oko*_*oko 7 python performance benchmarking python-3.x python-asyncio

我正在使用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))
Run Code Online (Sandbox Code Playgroud)

输出:

版本A = Asyncio; 版本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
Run Code Online (Sandbox Code Playgroud)

lee*_*ech 14

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

试试这个:

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))
Run Code Online (Sandbox Code Playgroud)

这是我运行时得到的:

$ python test.py 
200
...
Version A: 0.43438172340393066
200
...
Version B: 1.6541109085083008
Run Code Online (Sandbox Code Playgroud)

速度要快得多,但实际上这只是产生线程并等待http库完成,你不需asyncio要这样做.

您可能希望结帐,aiohttp因为它是为了使用而构建的asyncio.requests是一个神话般的图书馆,但它不是为了asyncio.

  • @okoboko如果您打算使用`requests`,那么除非您的项目中有其他组件实际设计用于`asyncio`,否则不需要使用`asyncio`.如果是这种情况,你应该支持`aiohttp`而不是`requests`,除非你需要`aiohttp`中缺少`requests`的功能. (2认同)
  • 使用`loop.run_in_executor`的一个注意事项 - 当你使用默认的执行器(通过传递`None`作为第一个参数)时,你正在使用带有五个线程的`concurrent.futures.ThreadPoolExecutor`.这意味着您只能同时运行五个请求,这对于I/O绑定工作负载来说非常低.如果使用更多线程创建自己的ThreadPoolExecutor,您可能会获得更好的性能. (2认同)

bru*_*ard 7

为了完整起见,这是一个非常快速的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()
Run Code Online (Sandbox Code Playgroud)

代码是python 3.5及更高版本.

~> python asyncioreq.py
200
...
Aiohttp version:  0.15974688529968262
Run Code Online (Sandbox Code Playgroud)

希望有人可以使用它;)

  • 等待session.close()或将其用作上下文管理器.我得到了一个错误,即session.close()从未等待过. (2认同)