Asyncio 使 HTTP 请求变慢?
Asyncio Making HTTP Requests Slower?
我正在使用 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
您正在等待每个请求完成,然后再开始下一个请求。所以你有事件循环的开销,没有任何好处。
试试这个:
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 实现
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
希望有人可以使用它;)
基于@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()
我正在使用 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
您正在等待每个请求完成,然后再开始下一个请求。所以你有事件循环的开销,没有任何好处。
试试这个:
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 实现
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
希望有人可以使用它;)
基于@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()