Windows Azure Storage 之 Retry Policy (用来处理短暂性错误-Transient Fault)
在使用Windows Azure Storage Service 的时候, 通常会遇到各种各样的问题。
例如网络连接不稳定,导致请求没有发出去。删除一个Blob Container 之后又立刻创建同名的Container。
这些情况都有可能使得你的代码抛出异常。这种异常我们通常叫他:Transient Fault 翻译成中文就是短暂性异常。
处理这种异常最简单有效的途径就是过几秒钟之后再重新发送请求。
Retry Policy就是用来帮助我们完成这样一项工作的。
Retry Policy中自带三个已经封装好了可以直接使用的类,分别有着不同的重试策略。
1.LinearRetry
这个类提供一个平行线式的重试策略
上图展示了LinearRetry的重试策略。它一共重试了五次,每一次的时间间隔都是5秒,第六次由于已经超过了预设定的重试次数,所以就放弃了。
使用LinearRetry的方法如下。
static string accountName = "<storage account name>"; static string accountKey = "<storage account key>"; static void Main(string[] args) { var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true); string blobContainerName = "temp-" + DateTime.UtcNow.Ticks; CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); IRetryPolicy exponentialRetryPolicy = new ExponentialRetry(TimeSpan.FromSeconds(2), 10); blobClient.RetryPolicy = exponentialRetryPolicy; CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName); blobContainer.Create(); Console.WriteLine("Blob container created."); Console.ReadLine(); }
2.ExponentialRetry
这个类提供了一个递增式的重试策略
很明显这是一个指数级别的重试策略,每一次重试的时间间隔都是之前的2倍。
使用ExponentialRetry方法如下:
static string accountName = "<storage account name>"; static string accountKey = "<storage account key>"; static void Main(string[] args) { var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true); string blobContainerName = "temp-" + DateTime.UtcNow.Ticks; CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); IRetryPolicy linearRetryPolicy = new LinearRetry(TimeSpan.FromSeconds(2), 10); blobClient.RetryPolicy = linearRetryPolicy; CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName); IRetryPolicy noRetryPolicy = new NoRetry(); BlobRequestOptions requestOptions = new BlobRequestOptions() { RetryPolicy = noRetryPolicy, }; blobContainer.Create(requestOptions); Console.WriteLine("Blob container created."); Console.ReadLine(); }
3.NoRetry
这个类的重试策略就是不重试,这个策略看似矛盾,其实它是用来配合之前两种策略来使用的,当你希望一个Blob Container的所有操作都能应用重试策略,唯独Create Blob不使用重试策略的时候你就需要用到NoRetry了。 代码如下:
static string accountName = "<storage account name>"; static string accountKey = "<storage account key>"; static void Main(string[] args) { var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true); string blobContainerName = "temp-" + DateTime.UtcNow.Ticks; CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); IRetryPolicy linearRetryPolicy = new LinearRetry(TimeSpan.FromSeconds(2), 10); blobClient.RetryPolicy = linearRetryPolicy; CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName); IRetryPolicy noRetryPolicy = new NoRetry(); BlobRequestOptions requestOptions = new BlobRequestOptions() { RetryPolicy = noRetryPolicy, }; blobContainer.Create(requestOptions); Console.WriteLine("Blob container created."); Console.ReadLine(); }
除了以上三个已经封装好的类以外,我们还可以自己创建自定义的重试策略类, 我们只需要实现IRetryPolicy接口即可。
实现自定义类的一个好处是可以自定义控制在何种特定情况下才进行重试。
下面代码将自定义一个实现了IRetryPolicy接口的 自定义类。
public class ContainerBeingDeletedRetryPolicy : IRetryPolicy { int maxRetryAttemps = 10; TimeSpan defaultRetryInterval = TimeSpan.FromSeconds(5); public ContainerBeingDeletedRetryPolicy(TimeSpan deltaBackoff, int retryAttempts) { maxRetryAttemps = retryAttempts; defaultRetryInterval = deltaBackoff; } public IRetryPolicy CreateInstance() { return new ContainerBeingDeletedRetryPolicy(TimeSpan.FromSeconds(2), 5); } public bool ShouldRetry(int currentRetryCount, int statusCode, Exception lastException, out TimeSpan retryInterval, OperationContext operationContext) { retryInterval = defaultRetryInterval; if (currentRetryCount >= maxRetryAttemps) { return false; } //Since we're only interested in 409 status code, let's not retry any other operation. if ((HttpStatusCode)statusCode != HttpStatusCode.Conflict) { return false; } //We're only interested in storage exceptions so if there's any other exception, let's not retry it. if (lastException.GetType() != typeof(StorageException)) { return false; } else { var storageException = (StorageException)lastException; string errorCode = storageException.RequestInformation.ExtendedErrorInformation.ErrorCode; if (errorCode.Equals("ContainerBeingDeleted")) { return true; } else { return false; } } return true; } }
使用代码如下:
static string accountName = "<storage account name>"; static string accountKey = "<storage account key>"; static void Main(string[] args) { var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true); string blobContainerName = "temp-" + DateTime.UtcNow.Ticks; CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); IRetryPolicy linearRetryPolicy = new LinearRetry(TimeSpan.FromSeconds(2), 10); blobClient.RetryPolicy = linearRetryPolicy; CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName); blobContainer.Create(); Console.WriteLine("Blob container created."); blobContainer.Delete(); Console.WriteLine("Blob container deleted."); IRetryPolicy containerBeingDeletedRetryPolicy = new ContainerBeingDeletedRetryPolicy(TimeSpan.FromSeconds(2), 10); BlobRequestOptions requestOptions = new BlobRequestOptions() { RetryPolicy = containerBeingDeletedRetryPolicy, }; blobContainer.Create(requestOptions); Console.WriteLine("Blob container created."); Console.ReadLine(); }
这个类只用于处理返回409错误并且错误信息 "Blob container created." 的错误,其他错误则忽略掉了。
总结:
像上面这种,在一个方法中删除一个container并且重建同名container的情况很容易就产生 Transient Fault。
这时使用RetryPolicy就能够很好的解决这种异常了!