Pythonのasyncioで非同期にリクエストを飛ばす
pythonPythonのasyncioはイベントループを回してシングルスレッドで並行に非同期処理を行う。 マルチスレッドで並列に実行するのがthreadingで、 マルチプロセスで並列に実行するのがmultiprocessing。
import asyncio
async def sleep(s):
await asyncio.sleep(s)
print(s)
return s
loop = asyncio.get_event_loop()
loop.run_until_complete(sleep(5))
coros = [sleep(3), sleep(2)]
futures = asyncio.gather(*coros)
loop.run_until_complete(futures)
print(futures.result())
loop.close()
$ python main.py
5
2
3
[3, 2]
get_event_loop() でイベントループを取得し、 gather()で処理をまとめたりして、 run_until_complete()で Futureの完了を待ち、 結果を取得してイベントループを close() している。
async def を付けた関数はCoroutineとなり、 ensure_future()でFutureのサブクラスの、イベントループで実行させるTaskにすることができる。run_until_complete() はそのままCoroutineを投げても ensure_future() でwrapしてくれる。
httpクライアントrequestsはBlockingするようなので、asyncioに対応しているaiohttpを使ってリクエストしてみる。
import aiohttp
import asyncio
import async_timeout
async def fetch(session, url):
print("{} start".format(url))
async with async_timeout.timeout(10):
async with session.get(url) as response:
text = await response.text()
print("{} done".format(url))
return text
async def main():
async with aiohttp.ClientSession() as session:
urls = [
'https://www.youtube.com',
'https://www.python.org',
'https://www.google.co.jp',
'https://www.facebook.com'
]
promises = [fetch(session, u) for u in urls]
await asyncio.gather(*promises)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
非同期にリクエストが飛び、返り次第処理が実行されている。
$ python req.py
https://www.python.org start
https://www.facebook.com start
https://www.youtube.com start
https://www.google.co.jp start
https://www.python.org done
https://www.google.co.jp done
https://www.facebook.com done
https://www.youtube.com done