你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Azure 服务的重试指南
大多数 Azure 服务和客户端 SDK 都包括重试机制。 不过,这些重试机制各不相同,这是因为每个服务都有不同的特征和要求,这样一来,各个重试机制都会针对特定服务进行优化。 本指南汇总了大多数 Azure 服务的重试机制功能,并介绍了如何使用、适应或扩展相应服务的重试机制。
有关如何处理临时故障、针对服务和资源重试连接和操作的一般指南,请参阅重试指南。
下表总结了本指南中介绍的 Azure 服务重试功能。
服务 | 重试功能 | 策略配置 | 范围 | 遥测功能 |
---|---|---|---|---|
Microsoft Entra ID | MSAL 库原生 | 嵌入到 MSAL 库中 | 内部 | 无 |
Azure Cosmos DB | 服务原生 | 不可配置 | 全球 | TraceSource |
数据湖存储区 | 客户端原生 | 不可配置 | 各项操作 | 无 |
事件中心 | 客户端原生 | 编程 | 客户端 | 无 |
IoT 中心 | 客户端 SDK 原生 | 编程 | 客户端 | 无 |
用于 Redis 的 Azure 缓存 | 客户端原生 | 编程 | 客户端 | TextWriter |
搜索 | 客户端原生 | 编程 | 客户端 | 适用于 Windows (ETW) 或自定义的事件跟踪 |
服务总线 | 客户端原生 | 编程 | 命名空间管理器、消息工厂和客户端 | ETW |
Service Fabric | 客户端原生 | 编程 | 客户端 | 无 |
使用 ADO.NET 的 SQL 数据库 | Polly | 声明性和编程 | 各个语句或代码块 | 自定义 |
使用 Entity Framework 的 SQL 数据库 | 客户端原生 | 编程 | 每个应用域均为全局 | 无 |
使用 Entity Framework Core 的 SQL 数据库 | 客户端原生 | 编程 | 每个应用域均为全局 | 无 |
存储 | 客户端原生 | 编程 | 客户端 和各项操作 | TraceSource |
注意
对于大多数 Azure 内置重试机制,目前尚无方法针对不同类型的错误或异常应用不同的重试策略。 应该配置可提供最佳平均性能和可用性的策略。 微调策略的一种方法是分析日志文件,以确定发生的临时故障的类型。
Microsoft Entra ID
Microsoft Entra ID 是一项全面的标识和访问管理云解决方案,集成了核心目录服务、高级标识监管、安全性和应用程序访问管理等各种功能。 Microsoft Entra ID 还为开发人员提供了身份管理平台,以便他们可以根据集中的策略和规则,控制应用程序访问情况。
注意
有关托管服务标识终结点的重试指南,请参阅如何使用 Azure VM 托管服务标识 (MSI) 进行令牌获取。
重试机制
Microsoft 身份验证库 (MSAL) 提供适用于 Microsoft Entra ID 的内置重试机制。 为避免意外锁定,建议第三方库和应用程序代码不要重试失败的连接,而应让 MSAL 处理重试。
重试使用指南
使用 Microsoft Entra ID 时,请注意以下指南:
- 如有可能,请使用 MSAL 库和内置支持进行重试。
- 在对 Microsoft Entra ID 使用 REST API 的情况下,如果结果代码为 429(太多请求)或 5xx 范围中的错误,请重试该操作。 请勿针对其他任何错误重试操作。
- 对于 429 错误,仅在 Retry-After 标头中指示的时间后重试。
- 对于 5xx 错误,应采用指数退避,第一次重试至少应比该响应晚 5 秒。
- 请勿对 429 和 5xx 以外的错误重试。
后续步骤
Azure Cosmos DB
Azure Cosmos DB 是一种完全托管的多模型数据库,支持无架构 JSON 数据。 它提供可配置的可靠性能、本机 JavaScript 事务处理,专为具有弹性延展能力的云构建而成。
重试机制
Azure Cosmos DB SDK 会在某些错误情况下自动重试,并且鼓励用户应用程序具有自己的重试策略。 有关错误情况和重试时间的完整列表,请参阅使用 Azure Cosmos DB SDK 设计可复原应用程序指南。
遥测
根据应用程序的语言,诊断和遥测将公开为关于操作响应的日志或提升属性。 有关详细信息,请参阅 Azure Cosmos DB C# SDK 和 Azure Cosmos DB Java SDK 中的“捕获诊断”部分。
Data Lake Store
Azure Data Lake Storage Gen2 使 Azure 存储成为在 Azure 上构建企业 Data Lake 的基础。 Azure Data Lake Storage Gen2 让你可以轻松管理海量数据。
Azure 存储文件 Data Lake 客户端库包括所有所需的功能,使开发人员、数据专家和分析师可以更轻松地存储任何大小、形状和速度的数据以及跨平台和语言进行各种类型的处理和分析。
重试机制
DataLakeServiceClient 让你可以操纵 Azure Data Lake 服务资源和文件系统。 存储帐户为 Azure Data Lake 服务提供顶级命名空间。 创建客户端时,可以提供连接到 Azure Data Lake 服务 (DataLakeClientOptions) 的客户端配置选项。 DataLakeClientOptions 包含一个 Retry 属性(继承自 Azure.Core.ClientOptions),,可对该属性进行配置(RetryOptions 类)。
遥测
监视使用情况和性能是 Azure 存储实现服务的重要部分。 示例包括频繁的操作、出现高延迟的操作或导致服务端限制的操作。
存储帐户的所有遥测数据都可以通过 Azure Monitor 中的 Azure 存储日志提供。 此功能将存储帐户与 Log Analytics 和事件中心集成,同时允许将日志存档到另一个存储帐户。 要查看指标和资源日志及其关联架构的完整列表,请参阅 Azure 存储监视数据参考。
事件中心
Azure 事件中心是一项超大规模遥测数据引入服务,可收集、转换和存储数百万个事件。
重试机制
Azure 事件中心客户端库中的重试行为由 EventHubClient
类上的 RetryPolicy
属性控制。 Azure 事件中心返回暂时性 EventHubsException
或 OperationCanceledException
时,默认策略会使用指数回退进行重试。 事件中心的默认重试策略是重试最多 9 次,指数退避时间最多为 30 秒。
示例
EventHubClient client = EventHubClient.CreateWithManagedIdentity(new Uri("sb://full_namespace_url", "entity_path");
client.RetryPolicy = RetryPolicy.Default;
后续步骤
IoT 中心
Azure IoT 中心是一项服务,用于连接、监视和管理那些开发物联网 (IoT) 应用程序的设备。
重试机制
Azure IoT 设备 SDK 可以检测网络、协议或应用程序中的错误。 此 SDK 根据错误类型来检查是否需执行重试。 如果错误是可恢复的,SDK 就会开始使用配置的重试策略进行重试。
默认的重试策略为“带随机抖动的指数回退”,但这是可以配置的。
策略配置
策略配置因语言而异。 有关详细信息,请参阅 IoT 中心重试策略配置。
后续步骤
用于 Redis 的 Azure 缓存
Azure Cache for Redis 是一项快速数据访问和低延迟缓存服务,基于常用的开源 Redis 缓存。 它是由 Microsoft 管理的安全服务,可通过 Azure 中的任意应用程序进行访问。
本部分中的指南假设你使用 StackExchange.Redis 客户端访问高速缓存。 Redis 网站上列出了其他合适的客户端,这些客户端可能具有不同的重试机制。
StackExchange.Redis 客户端通过单个连接使用多路复用。 建议的用法是,在应用程序启动时创建客户端实例,并对针对高速缓存执行的所有操作使用此实例。 因此,高速缓存连接只建立一次,且本部分中的所有指南均与此启动连接(而不是访问高速缓存的每个操作)的重试策略相关。
重试机制
StackExchange.Redis 客户端使用连接管理器类,此类通过一组选项进行配置,其中包括:
- ConnectRetry。 缓存连接失败的重试次数。
- ReconnectRetryPolicy。 要使用的重试策略。
- ConnectTimeout。 以毫秒为单位的最长等待时间。
策略配置
重试策略是以编程方式进行配置,方法为在连接到高速缓存前设置客户端的选项。 为此,可以创建 ConfigurationOptions 类的实例,填充其属性,然后将它传递到 Connect 方法。
内置类支持随机化重试间隔的线性(固定)延迟和指数回退。 还可以通过实现 IReconnectRetryPolicy 接口创建自定义重试策略。
下面的示例使用指数回退配置重试策略。
var deltaBackOffInMilliseconds = TimeSpan.FromSeconds(5).TotalMilliseconds;
var maxDeltaBackOffInMilliseconds = TimeSpan.FromSeconds(20).TotalMilliseconds;
var options = new ConfigurationOptions
{
EndPoints = {"localhost"},
ConnectRetry = 3,
ReconnectRetryPolicy = new ExponentialRetry(deltaBackOffInMilliseconds, maxDeltaBackOffInMilliseconds),
ConnectTimeout = 2000
};
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(options, writer);
或者,可以将选项指定为字符串,然后将其传递到 Connect 方法。 ReconnectRetryPolicy 属性不能这样设置,它只能通过代码设置。
var options = "localhost,connectRetry=3,connectTimeout=2000";
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(options, writer);
也可以在连接到缓存时直接指定选项。
var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,connectRetry=3");
有关详细信息,请参阅 StackExchange.Redis 文档中的 Stack Exchange Redis 配置。
下表显示了内置重试策略的默认设置。
上下文 | 设置 | 默认值 (v 1.2.2) |
含义 |
---|---|---|---|
配置选项 | ConnectRetry ConnectTimeout SyncTimeout ReconnectRetryPolicy |
3 最长 5000 毫秒,外加 SyncTimeout 1000 LinearRetry 5000 毫秒 |
初始连接操作期间重试连接的次数。 连接操作的超时(以毫秒为单位)。 重试尝试之间没有延迟。 允许同步操作进行的时间(以毫秒为单位)。 每 5000 毫秒重试一次。 |
注意
对于同步操作,可将 SyncTimeout
添加到端到端延迟,但将其值设置过低可能会导致超时时间过长。 有关详细信息,请参阅如何排查 Azure Cache for Redis。 一般应避免使用同步操作,而改用异步操作。 有关详细信息,请参阅 Pipelines and Multiplexers(管道和多路复用器)。
重试使用指南
使用 Azure Cache for Redis 时,请注意以下指南:
- StackExchange Redis 客户端管理自己的重试,但仅限在应用程序首次启动时建立的高速缓存连接。 可以配置连接超时、重试尝试次数以及重试建立此连接间隔的时间,但重试策略不适用于针对缓存执行的操作。
- 请考虑改为访问原始数据源进行回退,而不是使用大量重试尝试。
遥测
可以使用 TextWriter 收集连接(而不是其他操作)的相关信息。
var writer = new StringWriter();
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(options, writer);
生成的输出示例如下所示。
localhost:6379,connectTimeout=2000,connectRetry=3
1 unique nodes specified
Requesting tie-break from localhost:6379 > __Booksleeve_TieBreak...
Allowing endpoints 00:00:02 to respond...
localhost:6379 faulted: SocketFailure on PING
localhost:6379 failed to nominate (Faulted)
> UnableToResolvePhysicalConnection on GET
No masters detected
localhost:6379: Standalone v2.0.0, master; keep-alive: 00:01:00; int: Connecting; sub: Connecting; not in use: DidNotRespond
localhost:6379: int ops=0, qu=0, qs=0, qc=1, wr=0, sync=1, socks=2; sub ops=0, qu=0, qs=0, qc=0, wr=0, socks=2
Circular op-count snapshot; int: 0 (0.00 ops/s; spans 10s); sub: 0 (0.00 ops/s; spans 10s)
Sync timeouts: 0; fire and forget: 0; last heartbeat: -1s ago
resetting failing connections to retry...
retrying; attempts left: 2...
...
示例
下面的代码示例配置初始化 StackExchange.Redis 客户端时两次重试间的固定(线性)延迟。 此示例展示如何使用 ConfigurationOptions 实例设置配置。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace RetryCodeSamples
{
class CacheRedisCodeSamples
{
public async static Task Samples()
{
var writer = new StringWriter();
{
try
{
var retryTimeInMilliseconds = TimeSpan.FromSeconds(4).TotalMilliseconds; // delay between retries
// Using object-based configuration.
var options = new ConfigurationOptions
{
EndPoints = { "localhost" },
ConnectRetry = 3,
ReconnectRetryPolicy = new LinearRetry(retryTimeInMilliseconds)
};
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(options, writer);
// Store a reference to the multiplexer for use in the application.
}
catch
{
Console.WriteLine(writer.ToString());
throw;
}
}
}
}
}
下一个示例通过将选项指定为字符串来设置配置。 连接超时是指等待连接缓存的时间上限,不是指两次重试尝试间的延迟。 ReconnectRetryPolicy 属性只能通过代码设置。
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace RetryCodeSamples
{
class CacheRedisCodeSamples
{
public async static Task Samples()
{
var writer = new StringWriter();
{
try
{
// Using string-based configuration.
var options = "localhost,connectRetry=3,connectTimeout=2000";
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(options, writer);
// Store a reference to the multiplexer for use in the application.
}
catch
{
Console.WriteLine(writer.ToString());
throw;
}
}
}
}
}
有关更多示例,请参阅项目网站上的配置。
后续步骤
Azure 搜索
Azure 搜索可用于向网站或应用程序添加功能强大且复杂的搜索功能、快速轻松地优化搜索结果,并构造大量经过微调的排名模型。
重试机制
用于 .NET 的 Azure SDK 包含 Azure SDK 团队提供的一个新的 Azure.Search.Documents 客户端库,该客户端库的功能等同于上一个 Microsoft.Azure.Search 客户端库。
Microsoft.Azure.Search 中的重试行为由 SearchServiceClient 和 SearchIndexClient 类中的 SetRetryPolicy 方法控制。 Azure 搜索返回 5xx 或 408(请求超时)响应时具有指数回退的默认策略重试次数。
Azure.Search.Documents 中的重试行为由 Retry 属性中的 SearchClientOptions(SearchClient 构造函数的一部分)控制,Retry 属性属于 Azure.Core.RetryOptions 类(其中的所有配置都可用)。
遥测
跟踪 ETW,或通过注册自定义提供程序来实现。 有关详细信息,请参阅 AutoRest 文档。
服务总线
服务总线是云消息传送平台,可提供松散耦合的消息交换,同时提升应用程序各组件(无论是托管在云中还是在本地)的规模和复原能力。
重试机制
命名空间和某些配置详细信息取决于所使用的服务总线客户端 SDK 包:
程序包 | 说明 | 命名空间 |
---|---|---|
Azure.Messaging.ServiceBus | 适用于 .NET 的 Azure 服务总线客户端库 | Azure.Messaging.ServiceBus |
WindowsAzure.ServiceBus | 此包是旧版服务总线客户端库。 它需要安装 .NET Framework 4.5.2。 | Microsoft.Azure.ServiceBus |
重试使用指南
ServiceBusRetryOptions
属性指定 ServiceBusClient
对象的重试选项:
设置 | 默认值 | 含义 |
---|---|---|
CustomRetryPolicy | 用于代替单个选项值的自定义重试策略。 | |
延迟 | 0.8 秒 | 固定方法的重试尝试之间的延迟,或基于退避的方法的计算依据的延迟。 |
MaxDelay | 60 秒 | 重试尝试之间的最大可能延迟。 |
MaxRetries | 3 | 将关联的操作视为失败之前的最大重试次数。 |
模式 | 指数 | 用于计算重试延迟的方法。 |
TryTimeout | 60 秒 | 等待单个尝试完成的最大持续时间(无论是初始尝试还是重试)。 |
设置 Mode
属性以使用以下任何值配置 ServiceBusRetryMode:
属性 | 价值 | 说明 |
---|---|---|
指数 | 1 | 重试尝试将根据退避策略延迟,该策略规定每次尝试都会增加重试前的等待时间。 |
固定 | 0 | 重试尝试以固定的时间间隔进行;每个延迟都是一致的持续时间。 |
示例:
using Azure.Identity;
using Azure.Messaging.ServiceBus;
string namespace = "<namespace>";
string queueName = "<queue_name>";
// Because ServiceBusClient implements IAsyncDisposable, we'll create it
// with "await using" so that it is automatically disposed for us.
var options = new ServiceBusClientOptions();
options.RetryOptions = new ServiceBusRetryOptions
{
Delay = TimeSpan.FromSeconds(10),
MaxDelay = TimeSpan.FromSeconds(30),
Mode = ServiceBusRetryMode.Exponential,
MaxRetries = 3,
};
await using var client = new ServiceBusClient(namespace, new DefaultAzureCredential(), options);
遥测
服务总线收集与其他 Azure 资源相同类型的监视数据。 可以使用 Azure Monitor 监视 Azure 服务总线。
还可以使用服务总线 .NET 客户端库发送遥测数据。
示例
下面的代码示例演示如何使用 Azure.Messaging.ServiceBus
包执行以下操作:
- 使用新
ServiceBusClientOptions
为ServiceBusClient
设置重试策略。 - 使用
ServiceBusMessage
的新实例创建新消息。 - 使用
ServiceBusSender.SendMessageAsync(message)
方法将消息发送到服务总线。 - 使用表示为
ServiceBusReceivedMessage
对象的ServiceBusReceiver
接收。
using Azure.Identity;
using Azure.Messaging.ServiceBus;
string namespace = "<namespace>";
string queueName = "queue1";
// Because ServiceBusClient implements IAsyncDisposable, we'll create it
// with "await using" so that it is automatically disposed for us.
var options = new ServiceBusClientOptions();
options.RetryOptions = new ServiceBusRetryOptions
{
Delay = TimeSpan.FromSeconds(10),
MaxDelay = TimeSpan.FromSeconds(30),
Mode = ServiceBusRetryMode.Exponential,
MaxRetries = 3,
};
await using var client = new ServiceBusClient(namespace, new DefaultAzureCredential(), options);
// The sender is responsible for publishing messages to the queue.
ServiceBusSender sender = client.CreateSender(queueName);
ServiceBusMessage message = new ServiceBusMessage("Hello world!");
await sender.SendMessageAsync(message);
// The receiver is responsible for reading messages from the queue.
ServiceBusReceiver receiver = client.CreateReceiver(queueName);
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
string body = receivedMessage.Body.ToString();
Console.WriteLine(body);
后续步骤
Service Fabric
在 Service Fabric 群集中分布可靠服务可防止出现本文描述的大部分潜在暂时性错误。 但是,某些暂时性错误仍有可能发生。 例如,命名服务收到请求时可能正在进行路由更改,从而导致它引发异常。 如果同一请求在 100 毫秒后发出,则有可能成功。
Service Fabric 可在内部管理这种暂时性错误。 你可以在设置服务时使用 OperationRetrySettings
类配置某些设置。 以下代码展示了一个示例。 在大多数情况下,不必执行此操作,直接使用默认设置即可。
FabricTransportRemotingSettings transportSettings = new FabricTransportRemotingSettings
{
OperationTimeout = TimeSpan.FromSeconds(30)
};
var retrySettings = new OperationRetrySettings(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(1), 5);
var clientFactory = new FabricTransportServiceRemotingClientFactory(transportSettings);
var serviceProxyFactory = new ServiceProxyFactory((c) => clientFactory, retrySettings);
var client = serviceProxyFactory.CreateServiceProxy<ISomeService>(
new Uri("fabric:/SomeApp/SomeStatefulReliableService"),
new ServicePartitionKey(0));
后续步骤
使用 ADO.NET 的 SQL 数据库
SQL 数据库是一种托管的 SQL 数据库,具有各种大小,可作为标准(共享)和高级(非共享)服务提供。
重试机制
访问使用 ADO.NET 的 SQL 数据库时,其中没有内置重试支持。 不过,请求的返回代码可用于确定请求失败原因。 有关 SQL 数据库限制的详细信息,请参阅 Azure SQL 数据库资源限制。 有关相关错误代码的列表,请参阅 SQL 数据库客户端应用程序的 SQL 错误代码。
可使用 Polly 库为 SQL 数据库实现重试。 有关更多信息,请参阅使用 Polly 进行暂时性故障处理。
重试使用指南
访问使用 ADO.NET 的 SQL 数据库时,请注意以下指南:
- 选择适当的服务选项(共享或高级)。 鉴于共享服务器的其他租户的使用情况,共享实例的连接延迟和限制可能比平常要长。 如果需要更加可预测的性能和可靠的低延迟操作,请考虑选择高级选项。
- 请务必在适当的级别或作用域执行重试,以避免出现导致数据不一致的非幂等操作。 理想情况下,所有操作都应是幂等操作,这样就可以重复执行,而不会导致不一致。 如果并非如此,应在符合以下条件的级别或作用域执行重试:在一个操作失败时允许撤消所有相关更改;例如,从事务作用域内。 有关详细信息,请参阅云服务基础数据访问层 - 临时故障处理。
- 不建议结合使用固定间隔策略和 Azure SQL 数据库(交互式方案除外,在此类方案中,仅有几次重试的间隔很短)。 对于大多数方案,请考虑使用指数回退策略。
- 定义连接时,为连接和命令超时选择合适的值。 当数据库忙碌时,超时太短可能会导致连接过早失败。 超时太长则会导致在检测到失败的连接前等待太久,进而可能会妨碍重试逻辑正常运行。 超时值是端到端延迟的组成部分;它可以有效地增加每次重试尝试的重试策略中指定的重试延迟。
- 多次重试后关闭连接(即使使用指数回退重试逻辑,也是如此),并在新连接上重试操作。 在同一个连接上多次重试相同的操作可能是导致连接问题出现的一个因素。 有关此技术的示例,请参阅云服务基础数据访问层 - 临时故障处理。
- 当连接池正在使用中(默认情况)时,可能会从池中选择相同的连接,即使在关闭并重新打开连接后,也可能会出现这样的情况。 如果遇到这种情况,解决方法是调用 SqlConnection 类的 ClearPool 方法,将连接标记为不可重用。 不过,仅在几次连接尝试均失败,且遇到与失败的连接相关的某类临时故障(如 SQL 超时(错误代码 -2))时,才能这样做。
- 如果数据访问代码使用作为 TransactionScope 实例启动的事务,则重试逻辑应重新打开连接,并启动新的事务作用域。 因此,可重试代码块应包含事务的整个作用域。
请考虑从下列重试操作设置入手。 这些设置用于常规用途,应监视操作,并对值进行微调以适应自己的方案。
上下文 | 示例目标 E2E 最大延迟 |
重试策略 | 设置 | 值 | 工作原理 |
---|---|---|---|---|---|
交互式, UI, 或 foreground |
2 秒 | FixedInterval | 重试计数 重试间隔 首次快速重试 |
3 500 毫秒 是 |
第 1 次尝试 - 延迟 0 秒 第 2 次尝试 - 延迟 500 毫秒 第 3 次尝试 - 延迟 500 毫秒 |
背景 或批处理 |
30 秒 | ExponentialBackoff | 重试计数 最小回退 最大回退 增量回退 首次快速重试 |
5 0 秒 60 秒 2 秒 false |
第 1 次尝试 - 延迟 0 秒 第 2 次尝试 - 约延迟 2 秒 第 3 次尝试 - 约延迟 6 秒 第 4 次尝试 - 约延迟 14 秒 第 5 次尝试 - 约延迟 30 秒 |
注意
端到端延迟目标假设采用服务连接的默认超时。 如果指定更长的连接超时,则每次重试尝试都会将端到端延迟延长这一附加时间。
示例
本部分说明如何使用 Polly 通过在 Policy
类中配置的一组重试策略访问 Azure SQL 数据库。
以下代码展示了 SqlCommand
类上的扩展方法,该方法调用具有指数回退的 ExecuteAsync
。
public async static Task<SqlDataReader> ExecuteReaderWithRetryAsync(this SqlCommand command)
{
GuardConnectionIsNotNull(command);
var policy = Policy.Handle<Exception>().WaitAndRetryAsync(
retryCount: 3, // Retry 3 times
sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(200 * Math.Pow(2, attempt - 1)), // Exponential backoff based on an initial 200 ms delay.
onRetry: (exception, attempt) =>
{
// Capture some information for logging/telemetry.
logger.LogWarn($"ExecuteReaderWithRetryAsync: Retry {attempt} due to {exception}.");
});
// Retry the following call according to the policy.
await policy.ExecuteAsync<SqlDataReader>(async token =>
{
// This code is executed within the Policy
if (conn.State != System.Data.ConnectionState.Open) await conn.OpenAsync(token);
return await command.ExecuteReaderAsync(System.Data.CommandBehavior.Default, token);
}, cancellationToken);
}
可按以下方式使用此异步扩展方法。
var sqlCommand = sqlConnection.CreateCommand();
sqlCommand.CommandText = "[some query]";
using (var reader = await sqlCommand.ExecuteReaderWithRetryAsync())
{
// Do something with the values
}
后续步骤
使用 Entity Framework 6 的 SQL 数据库
SQL 数据库是一种托管的 SQL 数据库,具有各种大小,可作为标准(共享)和高级(非共享)服务提供。 Entity Framework 是一种对象关系映射程序,可方便 .NET 开发人员使用域特定对象处理关系数据。 开发人员无需再像往常一样编写大部分数据访问代码。
重试机制
在通过名为连接复原/重试逻辑的机制访问使用 Entity Framework 6.0 及更高版本的 SQL 数据库时,提供重试支持。 重试机制的主要功能包括:
- 主要抽象是 IDbExecutionStrategy 接口。 此接口:
- 定义同步和异步 Execute 方法。
- 定义可直接使用或可在数据库上下文中配置为默认策略的类(映射到提供程序名称或提供程序名称和服务器名称)。 如果已在上下文中进行配置,则重试会在各个数据库操作级别发生,其中一些可能适用于给定的上下文操作。
- 定义何时以及如何重试失败的连接。
- 它包括 IDbExecutionStrategy 接口的多个内置实现:
- 默认:不重试。
- SQL 数据库的默认值(自动):不重试,但检查异常,并根据建议包装它们以使用 SQL 数据库策略。
- SQL 数据库的默认值:指数(从基类继承),外加 SQL 数据库检测逻辑。
- 它实现了包含随机化的指数回退策略。
- 内置重试类有状态,但非线程安全。 不过,可以在当前操作完成后重用它们。
- 如果超出指定的重试计数,则结果会在新的异常中进行包装。 它不会加剧当前异常。
策略配置
在访问使用 Entity Framework 6.0 及更高版本的 SQL 数据库时,提供重试支持。 重试策略是以编程方式进行配置。 不能逐个操作更改配置。
在上下文中将策略配置为默认值时,需要指定一个用于根据需要新建策略的函数。 以下代码展示了如何创建重试配置类来扩展 DbConfiguration 基类。
public class BloggingContextConfiguration : DbConfiguration
{
public BlogConfiguration()
{
// Set up the execution strategy for SQL Database (exponential) with 5 retries and 4 sec delay
this.SetExecutionStrategy(
"System.Data.SqlClient", () => new SqlAzureExecutionStrategy(5, TimeSpan.FromSeconds(4)));
}
}
然后,可以将此指定为应用程序启动时,所有使用 DbConfiguration 实例的 SetConfiguration 方法的操作的默认重试策略。 默认情况下,EF 会自动发现和使用配置类。
DbConfiguration.SetConfiguration(new BloggingContextConfiguration());
可以使用 DbConfigurationType 属性为上下文类添加批注,从而为上下文指定重试配置类。 不过,如果只有一个配置类,则 EF 会使用它,而无需为上下文添加批注。
[DbConfigurationType(typeof(BloggingContextConfiguration))]
public class BloggingContext : DbContext
如果需要对特定操作使用不同的重试策略,或对特定操作禁用重试,则可以创建配置类,以便能够在 CallContext 中设置标志,从而挂起或交换策略。 配置类可以使用此标志切换策略,或禁用提供的策略并使用默认策略。 有关详细信息,请参阅暂停执行策略(EF6 及更高版本)。
对各个操作使用特定重试策略的另一种方法是,创建相应策略类的实例,并通过参数提供所需的设置。 然后,需要调用它的 ExecuteAsync 方法。
var executionStrategy = new SqlAzureExecutionStrategy(5, TimeSpan.FromSeconds(4));
var blogs = await executionStrategy.ExecuteAsync(
async () =>
{
using (var db = new BloggingContext("Blogs"))
{
// Acquire some values asynchronously and return them
}
},
new CancellationToken()
);
使用 DbConfiguration 类的最简单方法是在与 DbContext 类相同的程序集中找到它。 不过,如果需要在不同的方案(如使用不同的交互式重试策略和后台重试策略)中使用相同的上下文,则这样做并不适合。 如果不同的上下文在各个应用域中执行,则可以通过内置的支持在配置文件中指定配置类,也可以使用代码对它进行明确设置。 如果不同的上下文必须在同一应用域中执行,则需要使用自定义解决方案。
有关详细信息,请参阅基于代码的配置(EF6 及更高版本)。
下表显示了使用 EF6 时的内置重试策略的默认设置。
设置 | 默认值 | 含义 |
---|---|---|
策略 | 指数 | 指数退让。 |
MaxRetryCount | 5 | 最大重试次数。 |
MaxDelay | 30 秒 | 重试之间的最大延迟。 此值不影响延迟序列的计算方式。 它只定义上限。 |
DefaultCoefficient | 1 秒 | 指数退避计算的系数。 不能更改此值。 |
DefaultRandomFactor | 1.1 | 用于添加每个条目的随机延迟的乘数。 不能更改此值。 |
DefaultExponentialBase | 2 | 用于计算下一次延迟的乘数。 不能更改此值。 |
重试使用指南
访问使用 EF6 的 SQL 数据库时,请注意以下指南:
选择适当的服务选项(共享或高级)。 鉴于共享服务器的其他租户的使用情况,共享实例的连接延迟和限制可能比平常要长。 如果需要可预测的性能和可靠的低延迟操作,请考虑选择高级选项。
不建议结合使用固定间隔策略和 Azure SQL 数据库。 请改用指数回退策略,因为服务可能已过载,更长的延迟则可以让服务有更长时间进行恢复。
定义连接时,为连接和命令超时选择合适的值。 根据业务逻辑设计和全面测试来选择超时值。 可能需要随着数据量或业务流程的变化,陆续修改此值。 当数据库忙碌时,超时太短可能会导致连接过早失败。 超时太长则会导致在检测到失败的连接前等待太久,进而可能会妨碍重试逻辑正常运行。 超时值是端到端延迟的组成部分,尽管你无法轻松确定在保存上下文时会执行多少命令。 可以设置 DbContext 实例的 CommandTimeout 属性,以此来更改默认超时值。
Entity Framework 支持在配置文件中定义的重试配置。 不过,为了实现 Azure 上的最大灵活性,应考虑在应用程序内以编程方式创建配置。 重试策略的特定参数(如重试次数和重试间隔)可以存储在服务配置文件中,并在运行时用于创建相应的策略。 这样一来,无需重启应用程序,即可更改设置。
请考虑从下列重试操作设置入手。 无法指定重试尝试之间的延迟(固定值且以指数序列生成)。 只能指定最大值(如下所示);除非创建自定义重试策略。 这些设置用于常规用途,应监视操作,并对值进行微调以适应自己的方案。
上下文 | 示例目标 E2E 最大延迟 |
重试策略 | 设置 | 值 | 工作原理 |
---|---|---|---|---|---|
交互式, UI, 或 foreground |
2 秒 | 指数 | MaxRetryCount MaxDelay |
3 750 毫秒 |
第 1 次尝试 - 延迟 0 秒 第 2 次尝试 - 延迟 750 毫秒 第 3 次尝试 - 延迟 750 毫秒 |
背景 或批处理 |
30 秒 | 指数 | MaxRetryCount MaxDelay |
5 12 秒 |
第 1 次尝试 - 延迟 0 秒 第 2 次尝试 - 约延迟 1 秒 第 3 次尝试 - 约延迟 3 秒 第 4 次尝试 - 约延迟 7 秒 第 5 次尝试 - 约延迟 12 秒 |
注意
端到端延迟目标假设采用服务连接的默认超时。 如果指定更长的连接超时,则每次重试尝试都会将端到端延迟延长这一附加时间。
示例
以下代码示例定义了使用 Entity Framework 的简单数据访问解决方案。 它通过定义可扩展 DbConfiguration 的 BlogConfiguration 类的实例来指定重试策略。
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.SqlServer;
using System.Threading.Tasks;
namespace RetryCodeSamples
{
public class BlogConfiguration : DbConfiguration
{
public BlogConfiguration()
{
// Set up the execution strategy for SQL Database (exponential) with 5 retries and 12 sec delay.
// These values could be loaded from configuration rather than being hard-coded.
this.SetExecutionStrategy(
"System.Data.SqlClient", () => new SqlAzureExecutionStrategy(5, TimeSpan.FromSeconds(12)));
}
}
// Specify the configuration type if more than one has been defined.
// [DbConfigurationType(typeof(BlogConfiguration))]
public class BloggingContext : DbContext
{
// Definition of content goes here.
}
class EF6CodeSamples
{
public async static Task Samples()
{
// Execution strategy configured by DbConfiguration subclass, discovered automatically or
// or explicitly indicated through configuration or with an attribute. Default is no retries.
using (var db = new BloggingContext("Blogs"))
{
// Add, edit, delete blog items here, then:
await db.SaveChangesAsync();
}
}
}
}
有关使用实体框架重试机制的更多示例,请参阅连接复原/重试逻辑。
使用 Entity Framework Core 的 SQL 数据库
Entity Framework Core 是一种对象关系映射程序,可方便 .NET Core 开发人员使用域特定对象处理数据。 开发人员无需再像往常一样编写大部分数据访问代码。 此版 Entity Framework 从头开始编写,不自动从 EF6.x 继承所有功能。
重试机制
在通过名为连接复原能力的机制访问使用 Entity Framework Core 的 SQL 数据库时,提供重试支持。 连接复原能力在 EF Core 1.1.0 中引入。
主抽象是 IExecutionStrategy
接口。 SQL Server(包括 Azure SQL)的执行策略可识别能够重试的异常类型,并为最大重试次数、两次重试间的延迟等提供合理的默认值。
示例
以下代码在配置 DbContext 对象(表示与数据库的会话)时实现自动重试。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFMiscellaneous.ConnectionResiliency;Trusted_Connection=True;",
options => options.EnableRetryOnFailure());
}
以下代码展示如何使用执行策略在自动重试机制下执行事务。 该事务在委托中定义。 如果发生暂时性故障,执行策略会再次调用委托。
using (var db = new BloggingContext())
{
var strategy = db.Database.CreateExecutionStrategy();
strategy.Execute(() =>
{
using (var transaction = db.Database.BeginTransaction())
{
db.Blogs.Add(new Blog { Url = "https://blogs.msdn.com/dotnet" });
db.SaveChanges();
db.Blogs.Add(new Blog { Url = "https://blogs.msdn.com/visualstudio" });
db.SaveChanges();
transaction.Commit();
}
});
}
Azure 存储
Azure 存储服务包括 Blob 存储、文件以及存储队列。
Blob、队列和文件
ClientOptions 类是所有客户端选项类型的基类型,并公开各个常用的客户端选项,例如 Diagnostics、Retry 和 Transport。 要提供用于连接到 Azure 队列、Blob 和文件存储的客户端配置选项,必须使用相应的派生类型。 下一个示例使用派生自 ClientOptions 的 QueueClientOptions 类,将客户端配置为连接到 Azure 队列服务。 Retry 属性是一组选项,可以指定这些选项来影响重试方式以及重试故障的条件。
using System;
using System.Threading;
using Azure.Core;
using Azure.Identity;
using Azure.Storage;
using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;
namespace RetryCodeSamples
{
class AzureStorageCodeSamples {
public async static Task Samples() {
// Provide the client configuration options for connecting to Azure Queue Storage
QueueClientOptions queueClientOptions = new QueueClientOptions()
{
Retry = {
Delay = TimeSpan.FromSeconds(2), //The delay between retry attempts for a fixed approach or the delay on which to base
//calculations for a backoff-based approach
MaxRetries = 5, //The maximum number of retry attempts before giving up
Mode = RetryMode.Exponential, //The approach to use for calculating retry delays
MaxDelay = TimeSpan.FromSeconds(10) //The maximum permissible delay between retry attempts
},
GeoRedundantSecondaryUri = new Uri("https://...")
// If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for GET or HEAD requests during retries.
// If the status of the response from the secondary Uri is a 404, then subsequent retries for the request will not use the
// secondary Uri again, as this indicates that the resource may not have propagated there yet.
// Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
};
Uri queueServiceUri = new Uri("https://storageaccount.queue.core.windows.net/");
string accountName = "Storage account name";
string accountKey = "storage account key";
// Create a client object for the Queue service, including QueueClientOptions.
QueueServiceClient serviceClient = new QueueServiceClient(queueServiceUri, new DefaultAzureCredential(), queueClientOptions);
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellationToken = source.Token;
// Return an async collection of queues in the storage account.
var queues = serviceClient.GetQueuesAsync(QueueTraits.None, null, cancellationToken);
表支持
注意
WindowsAzure.Storage Nuget 包和 Microsoft.Azure.Cosmos.Table Nuget 包已弃用。 有关 Azure 表支持,请参阅 Azure.Data.Tables Nuget 包
重试机制
该客户端库基于 Azure Core 库,它是一个向其他客户端库提供横切服务的库。
客户端应用程序尝试向服务发送网络请求时,失败的原因有很多。 一些例子包括超时、网络基础结构故障、服务因节流/繁忙而拒绝请求、服务缩减导致服务实例终止、服务实例停机被另一版本取代、服务因未处理异常而崩溃等。 通过提供内置的重试机制(用户可以覆盖默认配置),我们的 SDK 和用户的应用程序可以很好地应对此类故障。 请注意,某些服务对每个请求收取实际费用,因此,如果使用者在省钱和复原能力之间选择了前者,他们应该能够完全禁用重试。
策略配置
重试策略是以编程方式进行配置。 该配置基于 RetryOption 类。 TableClientOptions 上有一个继承自 ClientOptions 的属性
var tableClientOptions = new TableClientOptions();
tableClientOptions.Retry.Mode = RetryMode.Exponential;
tableClientOptions.Retry.MaxRetries = 5;
var serviceClient = new TableServiceClient("<endpoint>", new DefaultAzureCredential(), tableClientOptions);
下表显示了内置重试策略的可能性。
RetryOption
设置 | 含义 |
---|---|
延迟 | 固定方法的重试尝试之间的延迟,或基于退避的方法的计算依据的延迟。 如果服务提供 Retry-After 响应头,则下一次重试将延迟头值指定的持续时间。 |
MaxDelay | 重试之间允许的最大延迟(如果服务没有提供 Retry-After 响应头)。 如果服务提供 Retry-After 响应头,则下一次重试将延迟头值指定的持续时间。 |
“模式” | 用于计算重试延迟的方法。 |
NetworkTimeout | 应用于单个网络操作的超时。 |
RetryMode
设置 | 含义 |
---|---|
指数 | 重试尝试将根据退避策略延迟,该策略规定每次尝试都会增加重试前的等待时间。 |
MaxDelay | 重试尝试以固定的时间间隔进行;每个延迟都是一致的持续时间。 |
遥测
查看日志的最简单方法是启用控制台日志记录。 若要创建将消息输出到控制台的 Azure SDK 日志侦听器,请使用 AzureEventSourceListener.CreateConsoleLogger 方法。
// Setup a listener to monitor logged events.
using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();
示例
在关闭存储仿真器的情况下执行以下代码示例后,我们将能够在控制台中查看有关重试的信息。
using Azure.Core;
using Azure.Core.Diagnostics;
using Azure.Data.Tables;
using Azure.Data.Tables.Models;
using Azure.Identity;
namespace RetryCodeSamples
{
class AzureStorageCodeSamples
{
private const string tableName = "RetryTestTable";
public async static Task SamplesAsync()
{
// Setup a listener to monitor logged events.
using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();
var tableClientOptions = new TableClientOptions();
tableClientOptions.Retry.Mode = RetryMode.Exponential;
tableClientOptions.Retry.MaxRetries = 5;
var serviceClient = new TableServiceClient("<endpoint>", new DefaultAzureCredential(), tableClientOptions);
TableItem table = await serviceClient.CreateTableIfNotExistsAsync(tableName);
Console.WriteLine($"The created table's name is {table.Name}.");
}
}
}
常规 REST 和重试指南
访问 Azure 或第三方服务时,请注意以下指南:
使用管理重试的系统化方法(可能作为可重用代码),以便跨所有客户端和解决方案应用一致的方法。
如果目标服务或客户端没有内置的重试机制,请考虑使用重试框架(如 Polly)来管理重试。 这会帮助你实现一致的重试行为,并可能会为目标服务提供合适的默认重试策略。 不过,可能需要为具有非标准行为的服务创建自定义重试代码,无需根据异常来指明临时故障;或者,可能需要使用 Retry-Response 答复来管理重试行为。
临时检测逻辑视用于调用 REST 调用的实际客户端 API 而定。 某些客户端(如较新的 HttpClient 类)不会引发包含失败 HTTP 状态代码的已完成请求的异常。
从服务返回的 HTTP 状态代码可以帮助指明故障是否是临时故障。 可能需要检查客户端生成的异常或重试框架,以访问状态代码或确定对等的异常类型。 以下 HTTP 代码通常可指明重试是否合适:
- 408 请求超时
- 429 请求次数过多
- 500 内部服务器错误
- 502 错误的网关
- 503 服务不可用
- 504 网关超时
如果根据异常构建重试逻辑,则以下代码通常可指明出现了无法建立连接的临时故障:
- WebExceptionStatus.ConnectionClosed
- WebExceptionStatus.ConnectFailure
- WebExceptionStatus.Timeout
- WebExceptionStatus.RequestCanceled
如果服务处于不可用状态,则服务可以指明在 Retry-After 响应头或其他自定义头中进行重试前的相应延迟。 服务还可以发送其他信息,既能以自定义头的形式,也能嵌入响应内容中。
请勿针对表示客户端错误的状态代码(4xx 范围内的错误)进行重试,“408 请求超时”和“429 请求过多”除外。
在各种条件(如不同的网络状态和系统负载)下全面测试重试策略和机制。
重试策略
以下是典型的重试策略间隔类型:
指数。 此重试策略执行指定次数的重试,并使用随机指数回退方法来确定重试间隔。 例如:
var random = new Random(); var delta = (int)((Math.Pow(2.0, currentRetryCount) - 1.0) * random.Next((int)(this.deltaBackoff.TotalMilliseconds * 0.8), (int)(this.deltaBackoff.TotalMilliseconds * 1.2))); var interval = (int)Math.Min(checked(this.minBackoff.TotalMilliseconds + delta), this.maxBackoff.TotalMilliseconds); retryInterval = TimeSpan.FromMilliseconds(interval);
增量。 此重试策略具有指定的重试尝试次数以及增量的重试间隔。 例如:
retryInterval = TimeSpan.FromMilliseconds(this.initialInterval.TotalMilliseconds + (this.increment.TotalMilliseconds * currentRetryCount));
线性重试。 此重试策略执行指定次数的重试,并使用指定的固定重试间隔。 例如:
retryInterval = this.deltaBackoff;
使用 Polly 处理暂时性错误
Polly 是一个库,用于以编程方式处理重试和断路器策略。 Polly 项目隶属于 .NET Foundation。 对于客户端不对重试提供本机支持的服务,Polly 是一种有效的替代方法,它让用户无需编写可能难以正确实现的自定义重试代码。 Polly 还提供一种在出现错误时对其进行跟踪的方法,以便用户记录重试。
后续步骤
- Entity Framework 连接复原