多くの異なるサイトのリストに非常に高速に接続したい。これを非同期で行うために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)
(補足として:これを最適化する方法を誰かが知っていますか?)
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
。