2015-04-07 122 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 

希望有人能使用它;)