Tip
スロットリングは初めてですか? スロットリングとは何かと、その対処方法について学びましょう。
概要
目標:.NET HTTP 回復性ハンドラーが再試行し、バックオフし、予期したとおりにタイムアウトすることを確認します
時間: 15 分
プラグイン:GenericRandomErrorPlugin、 RetryAfterPlugin、 LatencyPlugin
前提条件:Dev Proxy を設定します。これは Microsoft.Extensions.Http.Resilience を使用する .NET アプリです
AddStandardResilienceHandler()にHttpClientを追加しました。 どのようにして動作していると分かりますか? 呼び出す API が任意のタイミングで失敗することはほとんどなく、HttpMessageHandler をモックする単体テストでは、テストしたい回復性パイプラインをスキップしてしまいます。
開発プロキシは、アプリと API の間に配置されます。 アプリにエラーと遅い応答が返されるため、アプリは変更されずに実行され、回復性ハンドラーは実際の HTTP 応答に反応します。 Dev Proxy の出力には、すべての試行が表示されます。
標準の回復性ハンドラーの動作
テストする前に、何を期待するかを知ってください。 既定のオプションでは、AddStandardResilienceHandler():
| Behavior | Default |
|---|---|
| 再試行: オン | HTTP 500 以上、408、429、 HttpRequestException、および TimeoutRejectedException |
| 再試行回数 | 3、指数バックオフとジッターで、2 秒から開始 |
Retry-After ヘッダー |
尊重されました。 ハンドラーは、API が指定した時間だけ待機します。 |
| 試行タイムアウト | 1 回の試行につき 10 秒 |
| 合計タイムアウト | リクエストには 30 秒 (すべての再試行を含む) |
戦略とその既定値の完全な一覧については、 標準の回復性ハンドラーの既定値に関するページを参照してください。
アプリをDev Proxy経由でルーティングする
.NETはシステム プロキシを使用するため、Dev Proxyを起動すると、コードを変更せずにアプリの要求がインターセプトされます。 詳細については、「.NET アプリケーションでの Dev Proxy の使用」を参照してください。
一時的なエラーをシミュレートする
ハンドラーが再試行するエラーを使用して API への要求をエラーで失敗させる開発プロキシ構成を作成します。 この例では、 https://api.contoso.comを使用します。 これを、アプリが呼び出す API の URL に置き換えます。
ファイル: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "GenericRandomErrorPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "transientErrors"
}
],
"urlsToWatch": [
"https://api.contoso.com/*"
],
"transientErrors": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
"errorsFile": "transient-errors.json",
"rate": 50
}
}
Caution
構成ファイルのRetryAfterPluginの前にGenericRandomErrorPluginを追加します。 後で追加した場合、GenericRandomErrorPluginが確認できるようになる前に、RetryAfterPluginがリクエストを失敗させます。
エラー ファイルで、スロットリング応答と 2 つのサーバー エラーを定義します。
@dynamic値は、Retry-After ヘッダーを設定し、アプリが API を再度呼び出す前にその時間待機していることを確認するようにRetryAfterPluginに指示します。
ファイル: transient-errors.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.contoso.com/*"
},
"responses": [
{
"statusCode": 429,
"headers": [
{
"name": "Retry-After",
"value": "@dynamic"
}
]
},
{
"statusCode": 500
},
{
"statusCode": 503
}
]
}
]
}
開発プロキシを起動し、アプリを実行します。
devproxy --config-file devproxyrc.json
失敗率が 50% の場合、ほとんどの要求は 1 回または 2 回の再試行後に回復します。 Dev Proxy の出力で、次のことを確認してください:
- 429 応答の後、同じ URL に対する次の試行は、
Retry-After時刻の後に行われます。 Dev Proxy では、既定で 5 秒が使用されます。 アプリが API を呼び出すのが早すぎる場合、RetryAfterPluginはそれを報告し、リクエストをスロットルします。 - アプリが送信する試行回数が、構成した回数を超えることはありません。
- 再試行したくないリクエスト(レコードを作成する
POSTなど)は、1 回だけ送信されます。 それらを除外するには、options.Retry.DisableForUnsafeHttpMethods()またはoptions.Retry.DisableFor(...)に電話します。
再試行がなくなった場合の動作をテストする
再試行すると、短時間の障害が見えにくくなります。 また、API呼び出しが失敗し続ける場合にアプリが何を行うかも知る必要があります。 100% 失敗率で開発プロキシを起動します。
devproxy --config-file devproxyrc.json --failure-rate 100
Dev Proxy では、要求ごとに 4 回の試行 (元の要求と 3 回の再試行) が表示されます。 最後の再試行の後、標準ハンドラーは例外をスローしません。 コードに最後のエラー応答が返されます。 アプリがそれを使って何をするか確認します。 たとえば、 EnsureSuccessStatusCode() は HttpRequestExceptionをスローし、 GetStringAsync() もスローします。 クラッシュや一般的なエラーの表示ではなく、アプリに役立つメッセージが表示されているか、フォールバックしていることを確認します。
テストタイムアウト
低速 API は、ハンドラー内の別の処理経路に入ります。 これをテストするには、LatencyPlugin を使用して、10 秒の試行タイムアウトを超えて応答を遅らせます。
ファイル: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "LatencyPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "slowApi"
}
],
"urlsToWatch": [
"https://api.contoso.com/*"
],
"slowApi": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/latencyplugin.schema.json",
"minMs": 11000,
"maxMs": 15000
}
}
開発プロキシを起動し、アプリを実行します。 各試行には 10 秒を超える時間がかかるため、試行タイムアウトによって試行は取り消され、ハンドラーが再試行します。 30 秒後、合計タイムアウトによって要求が取り消され、コードでは TimeoutRejectedException が返されます。 アプリがそれをキャッチし、何が起こったかをユーザーに伝えることを確認します。
Tip
独自の回復性設定で同じシナリオをテストするには、 AddStandardResilienceHandler(options => ...) 呼び出しの値を変更し、同じ開発プロキシ構成をもう一度実行します。
次のステップ
任意の API でのスロットリングのシミュレーションの詳細をご覧ください。
参照
- .NET アプリケーションで Dev Proxy を使用する - .NETセットアップ
- GenericRandomErrorPlugin - 完全なリファレンス
- RetryAfterPlugin - リトライ動作を確認する
- LatencyPlugin - 低速応答をシミュレートする
- 要求失敗率の変更 - 要求が失敗する頻度を調整する
- CI/CD で開発プロキシを使用 する - パイプラインで回復性テストを自動化する
Dev Proxy