在分布式系统和微服务架构中,网络抖动、服务短暂不可用、数据库死锁等问题时有发生。出错重试(Retry)​ 是保障系统稳定性的关键手段。本文从浅入深,覆盖从手工重试 → Polly 策略 → 最佳实践的全套方案。一、为什么要重试?常见的 transient 故障(瞬态故障)故障类型典型场景网络抖动HTTP 请求超时、DNS 解析失败服务过载503 Service Unavailable数据库连接池耗尽Timeout expired数据库死锁Deadlock victim限流熔断429 Too Many Requests第三方服务不稳定OPC UA / PLC 写入失败⚠️这些故障有一个共同特点:稍后重试很可能成功二、最简单的重试:手写循环(不推荐长期使用)2.1 基础版public async TaskOperationResult SaveWithRetryAsync(Data data) { int retryCount = 0; const int maxRetries = 3; while (true) { try { return await _service.SaveAsync(data); } catch (Exception ex) when (retryCount maxRetries) { retryCount++; // 指数退避:1s → 2s → 4s var delay = TimeSpan.FromSeconds(Math.Pow(2, retryCount - 1)); await Task.Delay(delay); } } }2.2 存在的问题❌ 代码侵入性强❌ 每个方法都要写一遍❌ 无法统一策略❌ 缺少超时、熔断等高级特性❌ 容易写出 bug(无限重试、异常吞噬)三、推荐方案:Polly —— .NET 弹性编程标准库3.1 安装dotnet add package Polly # 或用于 ASP.NET Core 扩展 dotnet add package Microsoft.Extensions.Http.Polly3.2 核心概念概念说明Policy单个处理策略(重试、超时、熔断等)PolicyWrap策略组合(如:重试 + 熔断 + 超时)Context执行上下文,传递参数Result包装执行结果四、基础重试策略4.1 简单重试using Polly; var retryPolicy = Policy .HandleHttpRequestException() .OrTimeoutException() .RetryAsync(3, onRetry: (exception, retryCount, context) = { Console.WriteLine($"第 {retryCount} 次重试,原因:{exception.Message}"); }); // 使用 var result = await retryPolicy.ExecuteAsync(async () = { return await httpClient.GetAsync("https://api.example.com/data"); });4.2 指数退避重试(推荐)var retryPolicy = Policy .HandleHttpRequestE