aiohttp
ライブラリを使用して非同期リクエストを行った時に、何回かに一回、asyncio.exceptions.TimeoutError
が発生しました。
ローカル環境ではサーバーでエラーが出なかったため、原因に思い至るまでに試行錯誤して時間がかかったので、メモしておきます。
HTTPリクエストのテストコード
以下に、aiohttp
を使用した非同期のGETリクエスト関数と、それに対するaioresponses
を使用したテストの例を示します。
test()はテストが通ります。
test_ngはaiohttp.client_exceptions.ClientConnectionError: Connection closed
が発生してテストが通りません。
違いは、コンテキストマネージャasyncの内と外のどちらでresponse.text()
を実行するかですね。
自分は最初、NGパターンの方でやっていて、相手先サーバーが正常にレスポンスを返しているログはあるのになんでタイムアウトエラーが出るんだろう?と不思議でしたが、書いてみるとasync with ブロックを抜けてからだとreadできないのがわかります。
import aiohttp
import pytest
from aioresponses import aioresponses
async def fetch_data_pattern_1(url):
async with aiohttp.ClientSession(timeout=1) as session:
async with session.get(url) as response:
return await response.text()
async def fetch_data_pattern_2(url):
async with aiohttp.ClientSession(timeout=1) as session:
response = await session.get(url)
return response
@pytest.mark.asyncio()
async def test():
mock_response_content = "Hello World!"
test_url = "http://example.com"
with aioresponses() as mocked:
mocked.get(
test_url,
body=mock_response_content,
)
response = await fetch_data_pattern_1(test_url)
assert response == mock_response_content
print(f"response: {response}")
@pytest.mark.asyncio()
async def test_ng():
mock_response_content = "Hello World!"
test_url = "http://example.com"
with aioresponses() as mocked:
mocked.get(
test_url,
body=mock_response_content,
)
response = await fetch_data_pattern_2(test_url)
data = await response.text()
assert data == mock_response_content