2015-04-20 25 views
12

Chcę bardzo szybko połączyć się z listą wielu różnych witryn. Im przy użyciu asyncio, aby to zrobić w asynchroniczny sposób, a teraz chcesz dodać limit czasu dla połączeń powinny być ignorowane, jeśli trwa zbyt długo, aby odpowiedzieć.Jak mogę dodać limit czasu połączenia z asyncio?

Jak mogę to wdrożyć?

import ssl 
import asyncio 
from contextlib import suppress 
from concurrent.futures import ThreadPoolExecutor 
import time 


@asyncio.coroutine 
def run(): 
    while True: 
     host = yield from q.get() 
     if not host: 
      break 

     with suppress(ssl.CertificateError): 
      reader, writer = yield from asyncio.open_connection(host[1], 443, ssl=True) #timout option? 
      reader.close() 
      writer.close() 


@asyncio.coroutine 
def load_q(): 
    # only 3 entries for debugging reasons 
    for host in [[1, 'python.org'], [2, 'qq.com'], [3, 'google.com']]: 
     yield from q.put(host) 
    for _ in range(NUM): 
     q.put(None) 


if __name__ == "__main__": 
    NUM = 1000 
    q = asyncio.Queue() 

    loop = asyncio.get_event_loop() 
    loop.set_default_executor(ThreadPoolExecutor(NUM)) 

    start = time.time() 
    coros = [asyncio.async(run()) for i in range(NUM)] 
    loop.run_until_complete(load_q()) 
    loop.run_until_complete(asyncio.wait(coros)) 
    end = time.time() 
    print(end-start) 

(Na marginesie: Ma ktoś pomysł jak zoptymalizować to?)

+0

Zapomniałeś "wydać' 'z wywołań do' q.put (None) 'wewnątrz' load_q', więc ten kod nie będzie działał tak jak jest obecnie zapisany. – dano

+0

nie potrzebujesz tutaj czytnika, pisarza. Możesz użyć 'asyncio.create_connection' z' Protocol', który nic nie robi (zamyka połączenie sieciowe, gdy tylko zostanie ustanowione). Oto przykład kodu, który wypróbowałem na pierwszej milionowej liście stron Alexa (http://stackoverflow.com/a/20722204/4279) (może być nieco przestarzały, np. Nie używa niektórych funkcji convieve takich jak ' asyncio.wait_for() '). Używa pojedynczego wątku i otwiera upto 'limit' ssl połączeń. – jfs

Odpowiedz

11

można zawinąć wywołanie open_connection w asyncio.wait_for, który pozwala określić timeout:

with suppress(ssl.CertificateError): 
     fut = asyncio.open_connection(host[1], 443, ssl=True) 
     try: 
      # Wait for 3 seconds, then raise TimeoutError 
      reader, writer = yield from asyncio.wait_for(fut, timeout=3) 
     except asyncio.TimeoutError: 
      print("Timeout, skipping {}".format(host[1])) 
      continue 

Należy pamiętać, że po podniesieniu TimeoutError, coroutine open_connection jest również anulowana. Jeśli nie chcesz, aby zostało ono anulowane (chociaż uważam, że chcesz je anulować w tym przypadku), to zawiń połączenie w asyncio.shield.

+0

, ale spowoduje to również zablokowanie połączenia? Podobnie jak otwarcie połączeń w pętli normalnej jeden po drugim. – ali

+0

@ali Nie, ponieważ wszystkie wywołania metody 'run' są zawijane w wywołaniu' asyncio.async', co oznacza, że ​​wszystkie działają jednocześnie. – dano

+1

Jeśli limit czasu połączenia musi znajdować się wewnątrz innej korporacji, zobacz [https://stackoverflow.com/questions/28609534/python-asyncio-force-timeout/48546189#48546189](Python asyncio force timeout) o stosie 'asyncio.ensure_future (asyncio.wait_for (create_connection())) ' –

Powiązane problemy