在现代软件开发中,错误处理是构建高可用、健壮系统的关键之一。尤其是当应用依赖外部服务(如 API、数据库或其他网络资源)时,临时的服务中断、超时或其他不可预见的错误都会影响应用的稳定性。为了提升系统的容错能力,Polly 成为 .NET 开发者的重要工具。Polly 是一个功能强大的 .NET 库,提供了多种常用的错误处理策略,如重试、断路器、回退等,帮助开发者在发生错误时进行优雅的恢复和处理。
本文将深入探讨 Polly 的核心功能及其在 .NET Core 中的应用,带你一步步掌握如何通过 Polly 构建健壮的错误处理机制。
1. 什么是 Polly?
Polly 是一个为 .NET 提供的开源库,旨在帮助开发者处理常见的错误处理需求。Polly 提供了一套丰富的策略,用于解决应用在与外部服务交互时可能出现的瞬时错误。它的主要功能包括:
-
重试(Retry)
-
断路器(Circuit Breaker)
-
回退(Fallback)
-
超时(Timeout)
-
并发限制(Bulkhead Isolation)
这些策略有助于在外部依赖出现问题时,保证系统能够优雅地恢复或避免过度依赖失败的服务。
2. Polly 核心策略
2.1 重试(Retry)
重试策略在遇到可恢复的错误时会自动重试失败的操作。比如,某个 HTTP 请求因为网络波动而失败,重试策略可以尝试再次发起请求,直到成功或达到最大重试次数。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var policy = Policy
.Handle<HttpRequestException>() // 捕获 HttpRequestException 异常
.RetryAsync(3); // 重试 3 次
HttpClient client = new HttpClient();
await policy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode(); // 请求失败会抛出异常
return response;
});
}
}
在上面的代码中,如果 HTTP 请求因某种原因失败,Polly 会自动重试最多 3 次。
2.2 断路器(Circuit Breaker)
断路器策略用于避免持续失败的操作对系统造成更大的影响。当一个操作连续失败多次时,断路器会启动,阻止进一步的请求执行,直到外部服务恢复正常。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var policy = Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync(2, TimeSpan.FromMinutes(1)); // 2 次失败后断路,1 分钟内不再尝试
HttpClient client = new HttpClient();
try
{
await policy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
return response;
});
}
catch (BrokenCircuitException)
{
Console.WriteLine("Circuit is broken. Requests are not being executed.");
}
}
}
在这个例子中,如果 HTTP 请求连续失败两次,断路器就会打开,接下来的请求将不会再发送,直到 1 分钟后恢复正常。
2.3 回退(Fallback)
回退策略是在操作失败时提供一个备选方案。通常用于在外部服务调用失败时,返回默认数据或缓存内容,而不是直接报错。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var fallbackPolicy = Policy
.Handle<HttpRequestException>()
.FallbackAsync(
Task.FromResult("Fallback response"), // 返回回退结果
onFallbackAsync: (outcome, context) =>
{
Console.WriteLine("Fallback triggered due to: " + outcome.Exception?.Message);
return Task.CompletedTask;
});
HttpClient client = new HttpClient();
var result = await fallbackPolicy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
});
Console.WriteLine(result); // 如果失败,返回回退结果
}
}
当请求失败时,FallbackAsync
会提供一个替代方案(这里是一个字符串“Fallback response”),从而避免应用崩溃。
2.4 超时(Timeout)
超时策略限制了操作的最大执行时间。如果操作超时,Polly 会自动终止该操作。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var timeoutPolicy = Policy
.TimeoutAsync(TimeSpan.FromSeconds(5)); // 设置请求最大超时时间为 5 秒
HttpClient client = new HttpClient();
try
{
var result = await timeoutPolicy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
});
}
catch (TimeoutRejectedException)
{
Console.WriteLine("The request timed out.");
}
}
}
在这个例子中,TimeoutAsync
设置了最大超时限制。如果请求没有在 5 秒内完成,将抛出 TimeoutRejectedException
。
2.5 并行限制(Bulkhead Isolation)
并行限制策略用于限制系统能够同时处理的并发操作数量。如果超过最大并发数,后续请求将被排队或拒绝。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var bulkheadPolicy = Policy
.BulkheadAsync(2, 4); // 最多 2 个并行请求,最多 4 个排队请求
HttpClient client = new HttpClient();
await Task.WhenAll(
bulkheadPolicy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
}),
bulkheadPolicy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
})
);
}
}
通过使用 BulkheadAsync
,你可以避免系统因过多并发请求而崩溃,限制并发请求的数量,确保系统的稳定性。
3. 策略组合与复合策略
Polly 还支持将多个策略组合成一个复合策略(Policy Wrap)。这样,你可以在一个操作中依次应用多个策略,比如先尝试重试,接着使用断路器,最后返回回退结果。
示例代码:
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var policy = Policy
.Handle<HttpRequestException>()
.RetryAsync(3)
.WrapAsync(
Policy.Handle<HttpRequestException>()
.CircuitBreakerAsync(2, TimeSpan.FromMinutes(1))
);
HttpClient client = new HttpClient();
try
{
await policy.ExecuteAsync(async () =>
{
var response = await client.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
return response;
});
}
catch (BrokenCircuitException)
{
Console.WriteLine("The circuit is broken.");
}
}
}
通过 WrapAsync
,你将重试策略和断路器策略结合起来,形成一个更复杂的容错处理机制。
4. 小结
Polly 为 .NET Core 应用提供了一种简洁而强大的错误处理机制。它的策略(重试、断路器、回退等)能够有效提高系统的稳定性,减少因外部服务不可用或临时故障而导致的系统崩溃。在实际开发中,合理利用 Polly 的策略,可以大大提升系统的健壮性和容错能力。
如果你还没有开始使用 Polly,不妨尝试在项目中引入它,特别是在与外部 API 或服务交互时,它能帮助你更好地处理瞬时故障,保证系统稳定运行。