Polly 是一个在 C# 中用于处置惩罚瞬态故障和提供弹性的库。它允许你以声明式的方式界说策略,如重试、熔断、超时、回退等,这些策略可以帮助你的代码在出现故障时保持稳健和可靠。
以下是怎样在 C# 中使用 Polly 实现重试策略的根本步骤:
- 首先,你需要在你的项目中安装 Polly 包。这可以通过 NuGet 包管理器来完成。在 Visual Studio 中,右键点击你的项目 -> 选择 "Manage NuGet Packages..." -> 搜刮 "
olly" -> 点击 "Install"。
- 在你的代码中,引入 Polly 命名空间:
- using Polly;
- using Polly.Retry;
复制代码
3.创建一个重试策略。你可以指定重试的次数、重试之间的等候时间等。比方,以下代码创建了一个在发生异常时最多重试 3 次的策略,每次重试之间等候 200 毫秒:- var retryPolicy = Policy
- .Handle<Exception>() // 指定要捕获的异常类型,这里捕获所有异常
- .WaitAndRetry( // 定义重试策略
- retryCount: 3, // 重试次数
- sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(200 * attempt) // 每次重试之间的等待时间
- );
复制代码 4.使用这个策略来执行大概会失败的操作。你可以使用 Execute 或 ExecuteAsync 方法来包装你的代码。以下是一个使用异步方法的例子:- public async Task<string> CallRemoteServiceWithRetryAsync(string url)
- {
- return await retryPolicy.ExecuteAsync(async () =>
- {
- using (var client = new HttpClient())
- {
- return await client.GetStringAsync(url); // 这里是可能会失败的操作
- }
- });
- }
复制代码 在这个例子中,假如 client.GetStringAsync(url) 方法抛出异常,Polly 会捕获这个异常,并根据你界说的重试策略来重试这个操作。
以上就是在 C# 中使用 Polly 实现重试策略的根本步骤。Polly 还支持其他类型的策略,如熔断、超时、舱壁隔离等,你可以根据你的需求来选择和使用这些策略。
什么是熔断策略和超时策略呢
熔断策略和超时策略是两种常见的容错和弹性设计谋略,用于处置惩罚分布式体系中的故障和延迟。
熔断策略(Circuit Breaker):
熔断策略是一种防止故障扩散的策略。当一个服务出现故障或超时,熔断器会打开并快速失败,拒绝后续的请求,避免请求堆积和资源耗尽。熔断器会临时屏蔽该服务,并在一段时间后尝试恢复。在分布式架构中,一个服务通常会与多个外部服务举行交互,这些外部服务的稳定性是无法绝对保证的。熔断策略就是应对这种三方服务不稳定的设计,它可以帮助体系在出现标题时保持高可用,防止故障进一步扩散,同时也能在一段时间后重新尝试恢复正常操作。
超时策略(Timeout):
超时策略针对的是前置条件,即凌驾一定的等候时间,想要得到成功的结果是不大概的,因此保证调用者不必等候超时。超时策略常用于控制客户端对服务调用的相应时间。在分布式体系中,假如某个服务调用因为某种原因(如网络延迟、服务故障等)无法在规定的时间内返回结果,客户端就会触发超时策略,停止等候并返回相应的错误信息。这样可以避免客户端长时间等候而导致资源浪费和性能下降。超时策略可以根据具体的业务需求和体系负载状态举行自适应地动态调解。
这两种策略都是为了提高分布式体系的可靠性和弹性,确保在出现故障或延迟时,体系可以或许快速地相应并恢复正常运行。
分布式体系中的故障和延迟是什么
在分布式体系中,故障和延迟是两个常见的挑战。
故障:
分布式体系中的故障通常可以分为物理故障和软件故障两大类。
- 物理故障:包罗硬件设备的损坏(如硬盘损坏、电源故障、网络设备故障等)、环境因素(如断电、断网、天然灾难等)以及硬件升级大概导致的体系不稳定。这些故障大概导致节点无法提供服务或节点间通讯中断。
- 软件故障:主要是由于软件设计或实现中的缺陷导致的,比方体系中的bug大概导致体系瓦解、负载过高等标题。软件故障还大概包罗操作体系或中间件软件的错误配置、不兼容等标题。
在分布式数据库体系中,常见的故障还包罗变乱故障、体系故障、介质故障和网络引起的故障。变乱故障大概由计算溢出、完备性破坏、操作员干预、输入输出报错等引起。体系故障大概涉及CPU堕落、死循环、缓冲区满了、体系瓦解等标题。介质故障则是指存放数据的介质(如磁盘)发生的故障。
延迟:
延迟是指体系相应请求所需的时间。在分布式体系中,由于网络传输、数据处置惩罚等因素的影响,大概会出现延迟标题。比方,网络延迟大概导致节点间通讯变慢,从而影响分布式应用的性能。此外,体系负载过高、资源竞争等因素也大概导致处置惩罚请求的延迟增长。
为了应对分布式体系中的故障和延迟标题,通常会采用一系列的策略和技能,如故障检测与恢复、负载均衡、缓存、异步处置惩罚、熔断、超时等。这些策略和技能旨在提高体系的可靠性、可用性和性能,确保体系可以或许在出现故障或延迟时仍然可以或许正常运行并提供服务。
什么是熔断器
熔断器(fuse),也被称为熔断体或保险丝,是一种电器安全装置。它的主要功能是当电路中的电流凌驾规定值时,通过自身产生的热量使熔体熔断,从而断开电路,以防止电路过载和短路引起的火灾和其他危险。熔断器的工作原理是利用电流的热效应,让导体内部升温到熔断点,从而切断电路。
熔断器广泛应用于高低压配电体系和控制体系以及用电设备中,如家庭电气设备(如电视、音响、电冰箱等)、工业电气设备和构建工程中的建筑电气安装等,作为短路和过电流的掩护器,是应用最广泛的掩护器件之一。熔断器的结构简单,使用方便,可以或许有用掩护电路的安全稳定运行。
熔断器的种类有很多,包罗插入式熔断器、螺旋式熔断器、封闭式熔断器、快速熔断器和自复熔断器等。每种熔断器都有其特定的应用场景和特点,比方快速熔断器主要用于半导体整流元件或整流装置的短路掩护,而自复熔断器则能限制短路电流但不能真正分断电路。
总之,熔断器是一种重要的电气掩护器件,可以或许在电路中起到关键的掩护作用。
怎样使用Polly库来创建一个简单的重试策略
首先,我们界说一个大概会失败的DoSomethingThatMightFail方法:- using System;
-
- public class ExampleService
- {
- private Random _random = new Random();
-
- public void DoSomethingThatMightFail()
- {
- // 模拟一个可能会失败的操作
- // 这里简单地通过随机数来决定是否抛出异常
- if (_random.Next(0, 3) == 0) // 假设有1/3的概率会失败
- {
- throw new Exception("Operation failed due to some error.");
- }
-
- // 如果没有抛出异常,则表示操作成功
- Console.WriteLine("Operation succeeded.");
- }
- }
复制代码 然后,我们使用Polly库来创建重试策略,并调用DoSomethingThatMightFail方法:- using System;
- using Polly;
-
- public class Program
- {
- public static void Main()
- {
- var retryPolicy = Policy
- .Handle<Exception>() // 指定要捕获的异常类型,这里捕获所有异常
- .WaitAndRetry( // 定义重试策略
- retryCount: 3, // 重试次数
- sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // 指数退避策略
- );
-
- var exampleService = new ExampleService();
-
- // 使用重试策略来执行可能会失败的方法
- retryPolicy.Execute(() =>
- {
- exampleService.DoSomethingThatMightFail();
- });
-
- Console.WriteLine("Operation completed with or without retries.");
- }
- }
复制代码 我们创建了一个ExampleService类,其中包含一个大概会抛出异常的DoSomethingThatMightFail方法。然后,在Main方法中,我们创建了一个重试策略retryPolicy,并使用该策略来调用exampleService.DoSomethingThatMightFail()方法。假如该方法在执行过程中抛出异常,Polly将捕获该异常并根据界说的策略举行重试。
注意,在这个例子中,我们没有处置惩罚Execute方法大概抛出的异常。在实际应用中,您大概需要添加适当的异常处置惩罚逻辑来确保步伐的健壮性。此外,您还可以根据需要使用Polly的其他策略,如熔断器、超时等。
怎样使用Polly库举行断路分析
首先,确保已经安装了Polly库。你可以通过NuGet包管理器来安装它:
然后,创建一个断路器策略:- using System;
- using Polly;
- using Polly.CircuitBreaker;
-
- public class CircuitBreakerExample
- {
- private static readonly IAsyncPolicy<HttpResponseMessage> _circuitBreakerPolicy = Policy<HttpResponseMessage>
- .Handle<Exception>() // 可以指定更具体的异常类型
- .CircuitBreakerAsync(
- handledEventsAllowedBeforeBreaking: 5, // 在断路器跳闸前允许通过的失败事件数
- durationOfBreak: TimeSpan.FromSeconds(30), // 断路器跳闸后保持打开的时间
- onBreak: (ex, breakTimeout) => // 当断路器跳闸时执行的回调
- {
- Console.WriteLine("The circuit is now open. Calls to the service will not be made for the next " +
- breakTimeout.TotalSeconds + " seconds.");
- },
- onReset: () => // 当断路器从跳闸状态恢复时执行的回调
- {
- Console.WriteLine("The circuit is now closed. Calls to the service are allowed again.");
- },
- onHalfOpen: () => // 当断路器从关闭状态变为半开状态时执行的回调
- {
- Console.WriteLine("The circuit is now half-open. Allowing a single call to see if the service is available.");
- }
- );
-
- public async Task CallServiceWithCircuitBreakerAsync()
- {
- // 假设HttpCallAsync是一个异步方法,它调用某个远程服务并返回HttpResponseMessage
- // 在这里我们使用一个模拟的异步方法来代替
- Func<Task<HttpResponseMessage>> httpCallAsync = async () =>
- {
- // 模拟服务调用可能会失败
- if (DateTime.Now.Second % 2 == 0) // 例如,每两秒失败一次
- {
- throw new Exception("Service call failed!");
- }
-
- // 模拟成功的服务响应
- return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
- };
-
- // 使用断路器策略来调用服务
- await _circuitBreakerPolicy.ExecuteAsync(httpCallAsync);
- }
-
- // 主方法或其他启动点
- public static void Main()
- {
- var example = new CircuitBreakerExample();
-
- // 假设我们多次调用服务
- for (int i = 0; i < 10; i++)
- {
- example.CallServiceWithCircuitBreakerAsync().Wait();
- // 等待一段时间以便观察断路器的行为
- System.Threading.Thread.Sleep(1000);
- }
- }
- }
复制代码
在上面的示例中,CircuitBreakerAsync方法配置了一个断路器策略。当连续发生5次失败时,断路器会跳闸,并在接下来的30秒内制止对服务的调用。当断路器跳闸时,它会执行onBreak回调。当断路器从跳闸状态恢复时,它会执行onReset回调。在断路器从关闭状态变为半开状态时,它会执行onHalfOpen回调。
请注意,上面的示例使用了一个模仿的异步服务调用httpCallAsync。在实际应用中,你应该更换为调用远程服务的真实代码。
此外,由于ExecuteAsync是一个异步方法,你应该在异步上下文中调用它,比方在一个async方法中。在上面的Main方法中,我使用了.Wait()来等候异步操作完成,但这通常不是推荐的做法,因为它会阻塞调用线程。在真实的应用步伐中,你应该使用async和await关键字来避免阻塞。
结果:

AggregateException异常是在你尝试等候一个或多个异步操作的结果时发生的,其中一个或多个操作抛出了异常。在你的例子中,CallServiceWithCircuitBreakerAsync 方法内部调用了一个模仿的服务调用 httpCallAsync,它有时会抛出一个 Service call failed! 的异常。由于这个异常没有被捕获或处置惩罚,它终极导致了 AggregateException。
当你使用 .Wait() 来等候一个异步方法完成时,假如该异步方法抛出了异常,那么异常会被封装在 AggregateException 中。这是.NET Framework和.NET Core中处置惩罚异步异常的方式。
要正确处置惩罚这个异常,你应该在异步上下文中使用 await 关键字,而不是 .Wait()。同时,你可以使用 try-catch 语句来捕获并处置惩罚异常。下面是一个修改后的示例,展示了怎样在异步方法中使用 await 和 try-catch:- using System;
- using System.Net.Http;
- using System.Threading.Tasks;
- using Polly;
- using Polly.CircuitBreaker;
-
- public class CircuitBreakerExample
- {
- private static readonly IAsyncPolicy<HttpResponseMessage> _circuitBreakerPolicy = Policy<HttpResponseMessage>
- .Handle<Exception>()
- .CircuitBreakerAsync(
- handledEventsAllowedBeforeBreaking: 5,
- durationOfBreak: TimeSpan.FromSeconds(30),
- onBreak: (ex, breakTimeout) =>
- {
- Console.WriteLine("The circuit is now open. Calls to the service will not be made for the next " +
- breakTimeout.TotalSeconds + " seconds.");
- },
- onReset: () =>
- {
- Console.WriteLine("The circuit is now closed. Calls to the service are allowed again.");
- },
- onHalfOpen: () =>
- {
- Console.WriteLine("The circuit is now half-open. Allowing a single call to see if the service is available.");
- }
- );
-
- public async Task CallServiceWithCircuitBreakerAsync()
- {
- // 假设HttpCallAsync是一个异步方法,它调用某个远程服务并返回HttpResponseMessage
- Func<Task<HttpResponseMessage>> httpCallAsync = async () =>
- {
- // 模拟服务调用可能会失败
- if (DateTime.Now.Second % 2 == 0) // 例如,每两秒失败一次
- {
- throw new Exception("Service call failed!");
- }
-
- // 模拟成功的服务响应
- return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
- };
-
- try
- {
- // 使用断路器策略来调用服务
- await _circuitBreakerPolicy.ExecuteAsync(httpCallAsync);
- }
- catch (BrokenCircuitException ex)
- {
- // 处理断路器跳闸的异常
- Console.WriteLine("Circuit is broken: " + ex.Message);
- }
- catch (Exception ex)
- {
- // 处理其他异常
- Console.WriteLine("An error occurred: " + ex.Message);
- }
- }
-
- public static async Task Main() // 注意这里使用了async Main
- {
- var example = new CircuitBreakerExample();
-
- // 假设我们多次调用服务
- for (int i = 0; i < 10; i++)
- {
- await example.CallServiceWithCircuitBreakerAsync();
- // 等待一段时间以便观察断路器的行为
- await Task.Delay(1000);
- }
- }
- }
复制代码 请注意,我修改了 Main 方法以使用 async Task Main,这是C# 7.1及更高版本支持的特性,它允许你编写返回 Task 或 Task 的 Main 方法。假如你的环境不支持这一点,你可以将 Main 方法保持为同步的,并使用 .GetAwaiter().GetResult() 来等候异步操作,但这通常不是推荐的做法,因为它大概导致死锁和其他标题。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |