2015-04-20 2 views
12

Ich möchte mich sehr schnell mit einer Liste von vielen verschiedenen Seiten verbinden. Ich benutze asyncio, um dies in einer asynchronen Art und Weise zu tun und jetzt hinzufügen ein Timeout für wenn Verbindungen ignoriert werden sollen, wenn sie zu lange dauern, um zu antworten.Wie kann ich ein Verbindungstimeout mit asyncio hinzufügen?

Wie implementiere ich das?

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) 

(Auf einer Nebenbemerkung: Hat jemand eine Idee, wie diese zu optimieren?)

+0

Sie haben vergessen, die Aufrufe von 'q.put (None)' in 'load_q' zu" ergeben ", so dass dieser Code nicht wie derzeit geschrieben funktioniert. – dano

+0

Sie brauchen hier nicht Reader, Writer. Sie könnten 'asyncio.create_connection' mit' Protocol' verwenden, das nichts tut (es schließt die Netzwerkverbindung, sobald es eingerichtet ist). Hier ist [Codebeispiel, dass ich auf Million Alexa Website-Liste oben versucht habe] (http://StackOverflow.com/a/20722204/4279) (es könnte etwas veraltet sein, zB, es verwendet nicht einige Convience-Funktionen wie ' asyncio.wait_for() '). Es verwendet einen einzelnen Thread und öffnet sich zu 'limit' ssl Verbindungen. – jfs

Antwort

11

Sie den Anruf zu open_connection in asyncio.wait_for wickeln können, die Ihnen ein Timeout angeben können:

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 

Beachten Sie, dass wenn TimeoutError ausgelöst wird, die open_connection Coroutine ebenfalls abgebrochen wird. Wenn Sie nicht möchten, dass es abgebrochen wird (obwohl ich denke, dass Sie tun möchten, dass es in diesem Fall abgebrochen wird), haben Sie den Anruf in asyncio.shield umbrechen.

+0

aber das wird es auch zu einem blockierenden Anruf Nein machen? Wie das Öffnen von Verbindungen in normalen Schleifen nacheinander. – ali

+0

@ali Nein, weil alle Aufrufe der 'run'-Methode in einen' asyncio.async'-Aufruf eingeschlossen sind, was bedeutet, dass alle gleichzeitig ausgeführt werden. – dano

+1

Wenn sich das Verbindungszeitlimit innerhalb einer anderen Coroutine befinden muss, lesen Sie [https://StackOverflow.com/questions/28609534/python-asyncio-force-timeout/48546189#48546189](Python asyncio force timeout] zum Stapeln von 'asyncio.ensure_future (asyncio.wait_for (create_connection())) ' –

Verwandte Themen