Pythonで複数のhttpリクエストを同時に投げる

  • 2020.11.30
  • Web
Pythonで複数のhttpリクエストを同時に投げる

あるプロダクトでGoogleのChat APIを使用した機能を作っていて、その際に、複数のAPIコールを並列で同時に投げたくて、そのやり方を調べて実装したのでメモ。

並列で複数処理を行う場合はasyncio.gatherを使う

Node.jsでいうPromise.all的な処理をPythonで行うには、asyncio.gatherを使います。以下、実装例です。

APIコールを呼び出す側のコード

import asyncio

tasks = [req(i) for i in range(3)]
results = await asyncio.gather(*tasks)

APIコールを行う側のコード(ここでは、APIコールの模擬として、time関数を使用)

import time

def req(i):
    print(i, "start")
    time.sleep(0.5)
    print(i, "end")
    return i

これだけで良いかと思いきや、結果は以下のようになり、並列でタスク実行できていないことがわかります。

0 start
0 end
1 start
1 end
2 start
2 end

なぜこのような結果になるかというと、Pythonのhttpリクエストで使用するrequestsというライブラリは、ノンブロッキングではないためです。

ブロッキングな処理を複数スレッドで実行するためにrun_in_executer()関数を使う

したがって、以下のようにrun_in_executer()関数を使うことで、別スレッドでの処理に変える必要があります。

APIコールを呼び出す側のコード

loop = asyncio.get_event_loop()
tasks = [async_req(loop, i) for i in range(3)]
resusts = await asyncio.gather(*tasks)

APIコールを行う側のコード(以下、別スレッドで実行するためのコードを追記)

async def async_req(loop, i):
    return await loop.run_in_executor(None, req, i)

このようにすると、以下のように並列で処理を実行できていることがわかります。

0 start
1 start
2 start
0 end
1 end
2 end