Polly 是一个.NET弹性和瞬态故障处理库,它允许开发者以流畅和线程安全的方式表达重试(Retry)、断路器(Circuit Breaker)、超时(Timeout)、隔板隔离(Bulkhead Isolation)和回退策略(Fallback)等策略。非常适合用于构建容错能力更强的应用程序。
Polly 的应用场景• 网络请求重试
• 第三方服务调用超时处理
• 服务降级
• 缓存实现以提高响应速度
Polly 的安装可以通过 Nuget 包管理器安装 Polly:
Install-Package PollyPolly的主要策略及使用1. 重试(Retry):当执行的方法发生异常时,可以按照指定的次数进行重试。Polly 允许你指定需要处理的异常类型,重试次数以及每次重试的回调函数。
using Polly;//RetryForever 表示一直重试//Retry 表示重试一次//Retry(n) 表示重试n次//WaitAndRetryAsync可实现等待100ms再试,还不行在等150ms再试Policy polly = Policy.Handle<Exception>().Retry(3);polly.Execute(() => { Console.WriteLine("开始");//DateTime.Now.Second%10!=0 重试错误的条件if (DateTime.Now.Second % 10 != 0) { Console.WriteLine("错误");throw new Exception("error"); } Console.WriteLine("====完成=====");});1. **断路器(Circuit Breaker)**:用于在连续的失败达到一定阈值后,停止执行操作,避免继续失败并允许服务有时间来恢复。
using Polly;var circuitBreakerPolicy = Policy .Handle<HttpRequestException>() .CircuitBreaker(// 故障阈值:连续失败次数达到3次将触发断路器开启3,// 采样时长:30秒内的失败将被计入断路器状态 TimeSpan.FromSeconds(30),// 断路器开启时执行的操作 onBreak: (ex, breakDelay) => { Console.WriteLine($"断路器由于 {ex.Message} 打开。" +$"等待 {breakDelay.TotalSeconds} 秒后再次尝试。");// 可以在这里添加额外的操作,如记录日志、记录指标等。 },// 可选:断路器重置时执行的操作 onReset: () => { Console.WriteLine("断路器已重置。"); },// 可选:断路器半开启时执行的操作 onHalfOpen: () => { Console.WriteLine("断路器处于半开启状态。"); });// 使用断路器策略的示例代码circuitBreakerPolicy.Execute(() =>{var http=new HttpClient();// 可能会抛出 HttpRequestException 的代码var response = http.GetAsync("https://www.example.com/api/data").Result; response.EnsureSuccessStatusCode();var content = response.Content.ReadAsStringAsync().Result; Console.WriteLine($"收到的响应:{content}");});1. 超时(Timeout):用于监控任务执行的时长,如果超出指定时长则认为任务执行失败,不会继续等待结果。Polly 中关于超时的两个策略:一个是悲观策略(Pessimistic),一个是乐观策略(Optimistic)。其中,悲观策略超时后会直接抛异常,而乐观策略则不会,而只是触发CancellationTokenSource.Cancel函数,需要等待委托自行终止操作。一般情况下,我们都会用悲观策略。
https://www.cnblogs.com/Mamba8-24/p/17094636.html
using Polly.Timeout;using Polly;// 创建一个超时策略,设置超时时间为5秒var timeoutPolicy = Policy.TimeoutAsync(5, TimeoutStrategy.Pessimistic);try{ // 执行策略包裹的方法 await timeoutPolicy.ExecuteAsync(async () => { // 模拟一个耗时操作,延迟6秒 Console.WriteLine("开始执行耗时操作..."); await Task.Delay(6000); // 使用异步等待 Console.WriteLine("耗时操作完成。"); });}catch (TimeoutRejectedException){ // 处理超时异常 Console.WriteLine("操作超时");}1. 回退(Fallback):当操作注定失败时,提供一个备用方案来替代失败的操作,从而挽救失败的操作。
var httpClient = new HttpClient();// 定义一个回退策略,当操作失败时,返回一个默认值var fallbackPolicy = Policy<string> .Handle<HttpRequestException>() .FallbackAsync(async (cancellationToken) => {// 这里定义回退操作,返回默认值 Console.WriteLine("Fallback action is executed.");return await Task.FromResult("Fallback response"); });try{var response = await fallbackPolicy.ExecuteAsync(async () => {var result = await httpClient.GetStringAsync("https://example.com");return result; });// 打印最终的响应结果 Console.WriteLine($"Response: {response}");}catch (Exception ex){ Console.WriteLine($"Request failed: {ex.Message}");}1. 隔板隔离策略(Bulkhead Isolation):用于限制并发操作数量的模式,以防止系统过载。这种策略将系统资源分隔成多个独立的部分(隔板),每个部分可以独立地处理请求,从而限制单个部分的资源消耗,保护整体系统的稳定性。
var httpClient = new HttpClient();// 定义一个隔板隔离策略,限制并发操作数为5,队列长度为10var bulkheadPolicy = Policy.BulkheadAsync<HttpResponseMessage>( maxParallelization: 5, maxQueuingActions: 10, onBulkheadRejectedAsync: async context => { Console.WriteLine("Bulkhead isolation: Too many requests.");await Task.CompletedTask; });var tasks = new Task<HttpResponseMessage>[15];for (int i = 0; i < tasks.Length; i++){ tasks[i] = bulkheadPolicy.ExecuteAsync(async () => {var response = await httpClient.GetAsync("https://example.com"); response.EnsureSuccessStatusCode();return response; });}try{var responses = await Task.WhenAll(tasks);foreach (var response in responses) { Console.WriteLine($"Response: {response.StatusCode}"); }}catch (Exception ex){ Console.WriteLine($"Request failed: {ex.Message}");}1. 缓存策略(Cache):对于数据更新周期长且频繁使用的数据,首次加载后缓存起来,后续直接从缓存中读取。
dotnet add package Polly.Caching.Memory
using Microsoft.Extensions.Caching.Memory;using Polly;using Polly.Caching.Memory;var httpClient = new HttpClient();var memoryCache = new MemoryCache(new MemoryCacheOptions());// 定义一个缓存策略,缓存时间为60秒var cachePolicy = Policy.CacheAsync<HttpResponseMessage>(new MemoryCacheProvider(memoryCache), TimeSpan.FromSeconds(60));try{// 第一次请求,将缓存结果var response1 = await cachePolicy.ExecuteAsync(async context => {return await httpClient.GetAsync("https://example.com"); }, new Context("cacheKey")); Console.WriteLine($"First request status code: {response1.StatusCode}");// 第二次请求,将从缓存中获取结果var response2 = await cachePolicy.ExecuteAsync(async context => {return await httpClient.GetAsync("https://example.com"); }, new Context("cacheKey")); Console.WriteLine($"Second request status code: {response2.StatusCode}");}catch (Exception ex){ Console.WriteLine($"Request failed: {ex.Message}");}1. 策略包装策略(Policy Wrap):不同的异常需要不同的策略,策略包装允许将不同的策略组合在一起,灵活应对不同的异常情况。
var httpClient = new HttpClient();// 定义重试策略var retryPolicy = Policy .Handle<HttpRequestException>() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, context) => { Console.WriteLine($"Retrying due to: {exception.Message}"); });// 定义断路器策略var circuitBreakerPolicy = Policy .Handle<HttpRequestException>() .CircuitBreakerAsync(2, TimeSpan.FromSeconds(30), onBreak: (exception, timespan) => { Console.WriteLine("Circuit breaker opened"); }, onReset: () => { Console.WriteLine("Circuit breaker closed"); });// 将策略包装起来var policyWrap = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);try{var response = await policyWrap.ExecuteAsync(async () => {var result = await httpClient.GetAsync("https://example.com"); result.EnsureSuccessStatusCode();return result; });// 打印响应结果 Console.WriteLine($"Response: {response.StatusCode}");}catch (Exception ex){ Console.WriteLine($"Request failed: {ex.Message}");}• 体验地址:https://malus.dotnetshare.com

• 体验地址:https://www.dotnetshare.com