你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
当服务变得不可用或繁忙时,频繁的客户端重试可能会阻止服务恢复并恶化问题。 无限期重试无效,因为请求通常仅在有限的时间内保持有效。
上下文和问题
在云中,服务有时会遇到问题,并变得对客户端不可用或强制实施限制或速率限制。 虽然客户端重试到服务的失败连接是一种很好的做法,但它们不应过于频繁或太长时间地重试。 短时间内重试不太可能成功,因为服务可能尚未恢复。 恢复期间的连接尝试过多可能会使服务不堪重负,并加剧原始问题。 这种情况有时被称为 惊群效应。
以下示例演示了客户端连接到基于服务器的 API 的方案。 如果请求未成功,客户端会立即重试,并始终重试。 此类行为通常比此示例更微妙,但同样的原则适用。
public async Task<string> GetDataFromServer()
{
while(true)
{
var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
if (result.IsSuccessStatusCode) break;
}
// ... Process result.
}
解决方案
客户端应用程序应遵循最佳做法来防止重试风暴。
限制重试次数和持续时间。
while(true)编写循环似乎很简单,但通常不想长时间重试。 导致启动请求的条件可能会更改。 大多数应用程序只需重试几秒钟或几分钟。在重试尝试之间暂停并增加等待时间。 如果服务不可用,则立即重试不太可能成功。 逐渐增加尝试之间的时间量,例如,使用指数退避策略。
正常处理服务故障。 如果服务未响应,请确定是否中止尝试,并向组件的用户或调用方返回错误。 设计应用程序时,请考虑这些失败方案。
使用 断路器模式。 此模式专为帮助避免重试风暴而设计。
遵循响应标头。 如果服务器提供
retry-after响应标头,则在指定的时间段过后不要尝试重试。使用官方 SDK 与 Azure 服务通信。 这些 SDK 通常具有内置的重试策略和保护措施,以防止引发重试风暴。 如果与没有 SDK 或 SDK 的服务通信不当处理重试逻辑,请考虑使用 Polly for .NET 等库,或者 重试 JavaScript 以正确处理重试逻辑,并避免自行编写代码。
当可用时,请使用抽象层。 如果使用支持它的环境,请使用服务网格或其他抽象层发送出站调用。 通常,这些工具(如 Dapr)支持重试策略,并自动遵循最佳做法,例如在重复尝试后退让。 此方法无需自行编写重试代码。
考虑批处理请求,并在可用时使用请求池。 许多 SDK 代表你处理请求批处理和连接池,这减少了应用程序所尝试的出站连接尝试总数。 但请避免过于频繁地重试这些连接。
服务还应保护自己免受重试风暴的冲击。
添加网关层以在事件期间阻止连接。 此方法遵循 Bulkhead 模式。 Azure 为不同类型的解决方案提供许多网关服务,包括 Azure Front Door、 Azure 应用程序网关和 Azure API 管理。
限制网关的请求。 此方法可防止后端组件因请求过多而不知所措。
向客户端发送信号。 限制时,发送回
retry-after标头以帮助客户端了解何时重新尝试其连接。 客户端不需要遵循这些标头,但许多人这样做。
注意事项
了解错误类型。 某些错误类型不指示服务失败,而是指示来自客户端的请求无效。 例如,如果客户端应用程序收到错误响应,则重试同一
400 Bad Request请求可能会无济于事,因为服务器已通知你请求无效。定义适当的重试持续时间。 客户端应考虑重新尝试连接的最适当的时间长度。 时间范围应符合业务需求,并能够合理地将错误返回给用户或调用方。 大多数应用程序只需重试几秒钟或几分钟。
检测问题
从客户端的角度来看,此问题的症状可能包括很长的响应或处理时间,以及指示反复尝试重试连接的遥测数据。
从服务的角度来看,此问题的症状可能包括在短时间内从一个客户端发出的多个请求,或从单个客户端恢复中断时发出的多个请求。 在发生故障后,服务也可能难以恢复。 或者,在故障修复后,服务可能存在正在进行的级联故障。
示例诊断
以下部分演示了从客户端和服务角度检测潜在重试风暴的一种方法。
使用客户端遥测标识模式
Application Insights 记录来自应用程序的遥测数据,并使这些数据可用于查询和可视化。 它将出站连接跟踪为依赖项,并允许用户访问和绘制此信息的图表,以确定客户端何时向同一服务发出多个出站请求。
以下屏幕截图显示了 Application Insights 门户中“指标”选项卡上的图形。 它显示按远程依赖项名称拆分的依赖项故障指标。 此方案在短时间内尝试到依赖项的连接失败超过 21,000 次。
使用服务器遥测标识模式
服务器应用程序可能能够检测来自单个客户端的大量连接。 在以下示例中,Azure Front Door 充当应用程序的网关,并配置为将所有请求 记录 到 Log Analytics 工作区。
若要识别在最后一天向应用程序发送大量请求的客户端 IP 地址,请在 Log Analytics 中运行以下 Kusto 查询。
AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc
如果在重试风暴期间运行此查询,则会显示来自单个 IP 地址的大量连接尝试。