概览
目标:确认 .NET HTTP 弹性处理程序按预期进行重试、退让并超时
时间: 15 分钟
插件:GenericRandomErrorPlugin、RetryAfterPlugin、LatencyPlugin
先决条件:设置 Dev Proxy,这是一个使用 Microsoft.Extensions.Http.Resilience 的 .NET 应用
您已将 AddStandardResilienceHandler() 添加到您的 HttpClient。 你怎么知道它能正常工作? 你调用的 API 很少会在你需要时失败,并且模拟 HttpMessageHandler 的单元测试会跳过你想要测试的复原管道。
开发代理位于应用和 API 之间。 它向应用返回错误和缓慢的响应,因此应用无需更改即可运行,复原处理程序会对真实的 HTTP 响应作出反应。 可在 Dev Proxy 输出中看到每次尝试。
标准弹性处理程序的作用
在测试之前,请了解预期内容。 使用默认选项, AddStandardResilienceHandler():
| Behavior | 默认 |
|---|---|
| 开启重试 | HTTP 500 及以上、408、429、HttpRequestException 和 TimeoutRejectedException |
| 重试次数 | 3,带指数退避和抖动,从 2 秒开始 |
Retry-After 标头 |
荣幸。 处理程序等待 API 要求的时长。 |
| 尝试超时 | 每次尝试 10 秒 |
| 总超时 | 请求耗时 30 秒,包括所有重试 |
有关策略及其默认值的完整列表,请参阅 标准复原处理程序默认值。
通过 Dev Proxy 路由应用
.NET使用系统代理,因此,启动 Dev Proxy 时,它会截获应用的请求,而无需更改代码。 有关详细信息,请参阅将 Dev Proxy 用于 .NET 应用程序。
模拟瞬时错误
创建一个开发代理配置,使对 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 检查它之前使请求失败。
在错误文件中,定义限流响应和两个服务器错误。
@dynamic 值设置 Retry-After 标头,并告诉 RetryAfterPlugin 检查应用是否在再次调用 API 之前等待了这么长时间。
文件: 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%,大多数请求在一两次重试后成功。 在开发代理输出中,检查是否:
- 在收到 429 响应后,对同一 URL 的下一次尝试会在
Retry-After时间之后进行。 默认情况下,Dev Proxy 使用 5 秒。 如果应用调用 API 太早,则RetryAfterPlugin会报告这一情况并限制请求。 - 你的应用发送的尝试次数不会超过你配置的次数。
- 你不想重试的请求(如
POST创建记录的请求)只发送一次。 若要排除它们,请使用options.Retry.DisableForUnsafeHttpMethods()或options.Retry.DisableFor(...)。
测试重试用完时会发生什么情况
重试会掩盖短暂故障。 此外,还需要了解 API 持续失败时应用执行的操作。 使用 100% 失败率启动开发代理:
devproxy --config-file devproxyrc.json --failure-rate 100
开发代理显示每个请求的 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应用程序配合使用 - .NET设置
- GenericRandomErrorPlugin - 完整引用
- RetryAfterPlugin - 验证重试行为
- LatencyPlugin - 模拟慢响应
- 更改请求失败率 - 调整请求失败发生的频率
- 在 CI/CD 中使用开发代理 - 在管道中自动执行复原测试