Asyncio使HTTP请求更慢?

时间:2015-04-07 15:43:04

标签: python performance python-3.x benchmarking 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))

输出:

版本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

3 个答案:

答案 0 :(得分: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))

这是我运行时得到的结果:

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

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

您可能希望结帐aiohttp,因为它是为asyncio生成的。 requests是一个神话般的图书馆,但不是asyncio

答案 1 :(得分: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()

代码是python 3.5及更高版本。

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

希望有人可以使用它;)

答案 2 :(得分: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()