在C#开发中,自动重试机制是处理临时故障的重要手段,当操作因为网络波动、服务短暂不可用等原因失败时,通过自动重试可以避免程序直接抛出异常,提升系统的健壮性。Polly作为C#生态中成熟的弹性处理库,提供了灵活的重试策略配置能力,能够满足不同场景的重试需求。

Polly基础准备
首先需要在项目中引入Polly库,可以通过NuGet包管理器安装,对应的包名称是Polly,安装完成后即可在代码中引用相关命名空间。
Polly的重试策略核心是通过Policy类构建对应的策略对象,然后执行需要重试的操作,策略会自动根据配置的规则判断是否进行重试。
基础重试策略实现
最基础的重试策略是指定固定的重试次数,当操作抛出异常时就进行重试,直到达到最大重试次数或者操作成功。
以下是一个简单的重试示例,模拟一个可能失败的操作,配置重试3次:
using Polly;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// 构建重试策略:重试3次,每次重试间隔1秒
var retryPolicy = Policy
.Handle<Exception>() // 捕获所有异常触发重试
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(1));
int callCount = 0;
// 执行需要重试的操作
await retryPolicy.ExecuteAsync(async () =>
{
callCount++;
Console.WriteLine($"第{callCount}次执行操作");
// 模拟前两次操作失败,第三次成功
if (callCount < 3)
{
throw new Exception("操作临时失败");
}
Console.WriteLine("操作执行成功");
});
}
}
带条件的重试策略
实际场景中我们可能不需要对所有异常都重试,比如只对网络相关的异常重试,对参数错误等逻辑异常直接抛出异常。这时候可以在Handle方法中指定具体的异常类型。
下面的示例只对HttpRequestException进行重试,其他异常直接抛出:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var retryPolicy = Policy
.Handle<HttpRequestException>() // 只对HTTP请求异常重试
.RetryAsync(2); // 最多重试2次,无间隔
var httpClient = new HttpClient();
try
{
await retryPolicy.ExecuteAsync(async () =>
{
// 模拟请求一个可能临时不可用的地址
var response = await httpClient.GetAsync("http://ipipp.com/api/test");
response.EnsureSuccessStatusCode();
Console.WriteLine("请求成功");
});
}
catch (Exception ex)
{
Console.WriteLine($"最终操作失败:{ex.Message}");
}
}
}
重试回调与间隔配置
Polly支持在每次重试时执行回调方法,方便记录重试日志或者做一些额外的处理,同时也支持自定义重试间隔,比如指数退避策略,避免频繁重试给服务造成更大压力。
以下示例实现了指数退避重试,并且每次重试时输出重试次数和间隔:
using Polly;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var retryPolicy = Policy
.Handle<Exception>()
.WaitAndRetryAsync(
3, // 最大重试次数
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指数退避间隔:2秒、4秒、8秒
onRetry: (exception, timespan, retryCount, context) =>
{
// 重试回调,输出重试信息
Console.WriteLine($"第{retryCount}次重试,间隔{timespan.TotalSeconds}秒,异常信息:{exception.Message}");
}
);
int failCount = 0;
await retryPolicy.ExecuteAsync(async () =>
{
failCount++;
if (failCount <= 3)
{
throw new Exception("临时故障");
}
Console.WriteLine("操作最终成功");
await Task.CompletedTask;
});
}
}
重试策略的复用
如果多个地方需要使用相同的重试策略,可以将策略定义为静态对象复用,避免重复创建策略的开销。
using Polly;
using System;
using System.Threading.Tasks;
public static class RetryPolicies
{
// 定义通用的HTTP请求重试策略
public static readonly AsyncRetryPolicy HttpRetryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt * 2));
}
class Program
{
static async Task Main()
{
var httpClient = new HttpClient();
// 复用定义好的重试策略
await RetryPolicies.HttpRetryPolicy.ExecuteAsync(async () =>
{
var response = await httpClient.GetAsync("http://ipipp.com/api/data");
response.EnsureSuccessStatusCode();
Console.WriteLine("请求完成");
});
}
}
注意事项
- 重试策略不适合处理永久性故障,比如接口地址错误、权限不足等问题,这类问题重试多少次都会失败,反而浪费资源。
- 重试间隔建议根据场景合理配置,避免过短的重试间隔导致服务压力增大,指数退避是比较通用的选择。
- 如果重试的操作有副作用,比如已经修改了部分数据,需要确认重试是否会导致数据不一致问题,必要时需要配合事务或者幂等性设计。