web-dev-qa-db-ja.com

asyncioで接続タイムアウトを追加するにはどうすればよいですか?

多くの異なるサイトのリストに非常に高速に接続したい。これを非同期で行うためにasyncioを使用していて、応答に時間がかかりすぎる場合に接続を無視する必要がある場合のタイムアウトを追加したいと思います。

これをどのように実装しますか?

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)

(補足として:これを最適化する方法を誰かが知っていますか?)

18
scpio

open_connectionへの呼び出しは asyncio.wait_for でラップできます。これにより、タイムアウトを指定できます。

    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

TimeoutErrorが発生すると、open_connectionコルーチンもキャンセルされることに注意してください。キャンセルしたくない場合(この場合、doキャンセルしたいと思いますが)、呼び出しをラップします asyncio.shield

19
dano