2015-04-07 81 views
5

我使用Asyncio和Requests来测试一系列HTTP请求。Asyncio使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 

回答

12

您正在等待每个请求完成,然后再开始下一个请求。所以你有没有好处的事件循环的开销。

试试这个:

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

+0

伟大的答案,帮助!让'asyncio'产生线程而不是直接执行它有什么好处? – okoboko

+2

@okoboko如果你打算使用'requests',那么实际上不需要使用'asyncio',除非你的项目中有其他的组件实际上是为'asyncio'使用而设计的。如果是这样的话,除非你需要'aiohttp'中缺少'request'的特性,否则你应该在'request'上面支持'aiohttp'。 – dano

+2

带'loop.run_in_executor'的一个注释 - 当您使用默认执行程序(通过传递'None'作为第一个参数)时,您使用带有五个线程的'concurrent.futures.ThreadPoolExecutor'。这意味着您只能同时运行五个请求,这对于I/O绑定的工作负载来说相当低。如果使用更多线程创建自己的ThreadPoolExecutor,您可能会获得更好的性能。 – dano

6

只是为了完整性,这里是一个非常快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() 
    session.close() 
    te = time.time() 
    print("Aiohttp version: " + str(te - ts)) 

loop = asyncio.get_event_loop() 
loop.run_until_complete(main(10)) 
loop.close() 

代码为蟒蛇3.5及更高版本。

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

希望有人能使用它;)