Asyncioとaiohttpライブラリを使用して大量のリクエスト(〜1000)を作成しようとしていますが、詳細な情報が見つからない問題が発生しています。
このコードを10個のURLで実行すると、問題なく実行されます。 100以上のURLで実行すると、壊れてRuntimeError: Event loop is closed
エラー。
import asyncio
import aiohttp
@asyncio.coroutine
def get_status(url):
code = '000'
try:
res = yield from asyncio.wait_for(aiohttp.request('GET', url), 4)
code = res.status
res.close()
except Exception as e:
print(e)
print(code)
if __name__ == "__main__":
urls = ['https://google.com/'] * 100
coros = [asyncio.Task(get_status(url)) for url in urls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))
loop.close()
スタックトレースは here にあります。
これから数時間、私はこれについて頭をぶつけてきましたので、どんな助けや洞察もいただければ幸いです。明らかに、これはまだ開かれているはずのイベントループが閉じられたことを示していますが、それがどのように可能かはわかりません。
そうです、 loop.getaddrinfo はThreadPoolExecutor
を使用してスレッドでsocket.getaddrinfo
を実行します。
asyncio.wait_for をタイムアウトとともに使用しています。つまり、res = yield from asyncio.wait_for...
は4秒後にasyncio.TimeoutError
を発生させます。次に、get_status
コルーチンがNone
を返し、ループが停止します。その後ジョブが終了すると、イベントループでコールバックをスケジュールしようとし、すでに閉じられているため、例外を発生させます。
バグは https://github.com/python/asyncio/issues/258 として報告されます。
迅速な回避策として、カスタムエグゼキューターを使用することをお勧めします。
loop = asyncio.get_event_loop()
executor = concurrent.futures.ThreadPoolExecutor(5)
loop.set_default_executor(executor)
プログラムを完了する前に
executor.shutdown(wait=True)
loop.close()