在进入SOA之后,我们的代码从本地方法调用变成了跨机器的通信。任何一个新技术的引入都会为我们解决特定的问题,都会带来一些新的问题。比如网络故障、依赖服务崩溃、超时、服务器内存与CPU等其它问题。正是因为这些问题无法避免,所以我们在进行系统设计、特别是进行分布式系统设计的时候以“Design For Failure”(为失败而设计)为指导原则。把一些边缘场景以及服务之间的调用发生的异常和超时当成一定会发生的情况来预先进行处理。

Design For Failure
1. 一个依赖服务的故障不会严重破坏用户的体验。
2. 系统能自动或半自动处理故障,具备自我恢复能力。

以下是一些经验的服务容错模式

  • 超时与重试(Timeout and Retry)

  • 限流(Rate Limiting)

  • 熔断器(Circuit Breaker)

  • 舱壁隔离(Bulkhead Isolation)

  • 回退(Fallback)

如果想详细了解这几种模式可以参考美团技术团队的总结:服务容错模式。我们今天要讲的是,thanks to the community 多谢社区, Polly已经为我们实现了以上全部的功能。Polly是一个C#实现的弹性瞬时错误处理库(resilience and transient-fault-handling library一直觉得这个英文翻译不是很好) 。在Polly中,对这些服务容错模式分为两类:

  • 错误处理fault handling :重试、熔断、回退

  • 弹性应变resilience:超时、舱壁、缓存

可以说错误处理是当错误已经发生时,防止由于该错误对整个系统造成更坏的影响而设置。而弹性应变,则在是错误发生前,针对有可能发生错误的地方进行预先处理,从而达到保护整个系统的目地。

Polly 错误处理使用三步曲

  • 定义条件: 定义你要处理的 错误异常/返回结果

  • 定义处理方式 : 重试,熔断,回退

  • 执行

先看一个简单的例子

// 这个例子展示了当DoSomething方法执行的时候如果遇到SomeExceptionType的异常则会进行重试调用。var policy = Policy.Handle<SomeExceptionType>()   // 定义条件 .Retry(); // 定义处理方式// 执行policy.Execute(() => DoSomething());

定义条件

我们可以针对两种情况来定义条件:错误异常和返回结果。

// 单个异常类型Policy.Handle<HttpRequestException>()// 限定条件的单个异常Policy.Handle<SqlException>(ex => ex.Number == 1205)// 多个异常类型Policy.Handle<HttpRequestException>().Or<OperationCanceledException>()// 限定条件的多个异常Policy.Handle<SqlException>(ex => ex.Number == 1205).Or<ArgumentException>(ex => ex.ParamName == "example")// Inner Exception 异常里面的异常类型 Policy.HandleInner<HttpRequestException>().OrInner<OperationCanceledException>(ex => ex.CancellationToken != myToken)

以及用返回结果来限定

// 返回结果加限定条件 Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)// 处理多个返回结果Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError).OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.BadGateway)// 处理元类型结果 (用.Equals)Policy.HandleResult<HttpStatusCode>(HttpStatusCode.InternalServerError).OrResult<HttpStatusCode>(HttpStatusCode.BadGateway)
// 在一个policy里面同时处理异常和返回结果。HttpStatusCode[] httpStatusCodesWorthRetrying = {HttpStatusCode.RequestTimeout, // 408HttpStatusCode.InternalServerError, // 500HttpStatusCode.BadGateway, // 502HttpStatusCode.ServiceUnavailable, // 503HttpStatusCode.GatewayTimeout // 504}; HttpResponseMessage result = Policy.Handle<HttpRequestException>().OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode)).RetryAsync(...).ExecuteAsync( /* some Func<Task<HttpResponseMessage>> */ )

定义处理方式

在这里使用的处理方式就是我们最开始说的服务容错模式,我们将介绍以下三种:重试、熔断、回退。

重试

重试很好理解,当发生某种错误或者返回某种结果的时候进行重试。Polly里面提供了以下几种重试机制

  • 按次数重试

  • 不断重试(直到成功)

  • 等待之后按次数重试

  • 等待之后不断重试(直到成功)

按次数重试

// 重试1次Policy.Handle<SomeExceptionType>().Retry()// 重试3(N)次Policy.Handle<SomeExceptionType>().Retry(3)// 重试多次,加上重试时的action参数Policy.Handle<SomeExceptionType>().Retry(3, (exception, retryCount) =>{        // 干点什么,比如记个日志之类的 });

不断重试

// 不断重试,直到成功Policy.Handle<SomeExceptionType>().RetryForever()// 不断重试,带action参数在每次重试的时候执行Policy.Handle<SomeExceptionType>().RetryForever(exception =>{        // do something });

等待之后重试

// 重试3次,分别等待1、2、3秒。Policy.Handle<SomeExceptionType>().WaitAndRetry(new[]{TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2),TimeSpan.FromSeconds(3)});

当然也可以在每次重试的时候添加一些处理,这里我们可以从上下文中获取一些数据,这些数据在policy启动执行的时候可以传进来。

Policy.Handle<SomeExceptionType>().WaitAndRetry(new[]{TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2),TimeSpan.FromSeconds(3)}, (exception, timeSpan, context) => {    // do something    });

把WiatAndRetry抱成WaitAndRetryForever()则可以实现重试直到成功。

熔断

熔断也可以被作为当遇到某种错误场景下的一个操作。以下代码展示了当发生2次SomeExceptionType的异常的时候则会熔断1分钟,该操作后续如果继续尝试执行则会直接返回错误 。

Policy.Handle<SomeExceptionType>()   .CircuitBreaker(2, TimeSpan.FromMinutes(1));

可以在熔断和恢复的时候定义委托来做一些额外的处理。onBreak会在被熔断时执行,而onReset则会在恢复时执行。

Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
Action onReset = () => { ... };
CircuitBreakerPolicy breaker = Policy.Handle<SomeExceptionType>().CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

熔断器状态

我们的CircuitBreakPolicy的State定义了当前熔断器的状态,我们也可能调用它的Isolate和Reset方法来手动熔断和恢复 。

CircuitState state = breaker.CircuitState;
  • Closed 关闭状态,允许执行

  • Open 自动打开,执行会被阻断

  • Isolate 手动打开,执行会被阻断

  • HalfOpen  从自动打开状态恢复中,在熔断时间到了之后从Open状态切换到Closed

// 手动打开熔断器,阻止执行breaker.Isolate(); // 恢复操作,启动执行 breaker.Reset();

回退(Fallback)

// 如果执行失败则返回UserAvatar.BlankPolicy.Handle<Whatever>().Fallback<UserAvatar>(UserAvatar.Blank)// 发起另外一个请求去获取值Policy.Handle<Whatever>().Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }// 返回一个指定的值,添加额外的处理操作。onFallbackPolicy.Handle<Whatever>().Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) => {        // do something});

执行polly policy

为我声明了一个Policy,并定义了它的异常条件和处理方式,那么接下来就是执行它。执行是把我们具体要运行的代码放到Policy里面。

// 执行一个Actionvar policy = Policy.Handle<SomeExceptionType>().Retry();policy.Execute(() => DoSomething());

这就是我们最开始的例子,还记得我们在异常处理的时候有一个context上下文吗?我们可以在执行的时候带一些参数进去

// 看我们在retry重试时被调用的一个委托,它可以从context中拿到我们在execute的时候传进来的参数 。var policy = Policy.Handle<SomeExceptionType>().Retry(3, (exception, retryCount, context) =>{        var methodThatRaisedException = context["methodName"];Log(exception, methodThatRaisedException);});policy.Execute(() => DoSomething(), new Dictionary<string, object>() {{ "methodName", "some method" }}
);

当然,我们也可以将Handle,Retry, Execute 这三个阶段都串起来写。

Policy.Handle<SqlException>(ex => ex.Number == 1205).Or<ArgumentException>(ex => ex.ParamName == "example").Retry().Execute(() => DoSomething());

Polly 弹性应变处理Resilience

我们在上面讲了Polly在错误处理方面的使用,接下来我们介绍Polly在弹性应变这块的三个应用: 超时、舱壁和缓存。

超时

Policy.Timeout(TimeSpan.FromMilliseconds(2500))

支持传入action回调

Policy.Timeout(30, onTimeout: (context, timespan, task) => {        // do something });

超时分为乐观超时与悲观超时,乐观超时依赖于CancellationToken ,它假设我们的具体执行的任务都支持CancellationToken。那么在进行timeout的时候,它会通知执行线程取消并终止执行线程,避免额外的开销。下面的乐观超时的具体用法 。

// 声明 PolicyPolicy timeoutPolicy = Policy.TimeoutAsync(30);
HttpResponseMessage httpResponse = await timeoutPolicy.ExecuteAsync(     async ct => await httpClient.GetAsync(endpoint, ct), CancellationToken.None
// 最后可以把外部的 CacellationToken附加到 timeoutPollcy的 CT上,在这里我们没有附加);

悲观超时与乐观超时的区别在于,如果执行的代码不支持取消CancellationToken,它还会继续执行,这会是一个比较大的开销。

Policy.Timeout(30, TimeoutStrategy.Pessimistic)

上面的代码也有悲观sad…的写法

Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Pessimistic);var response = await timeoutPolicy.ExecuteAsync(    async () => await FooNotHonoringCancellationAsync(), );// 在这里我们没有 任何与CancllationToken相关的处理

舱壁

在开头的那篇文章中详细解释了舱壁这种模式,它用来限制某一个操作的最大并发执行数量 。比如限制为12

Policy.Bulkhead(12)

同时,我们还可以控制一个等待处理的队列长度

Policy.Bulkhead(12, 2)

以及当请求执行操作被拒绝的时候,执行回调

Policy.Bulkhead(12, context => {        // do something });

缓存

Polly的缓存需要依赖于一个外部的Provider。

var memoryCacheProvider = new Polly.Caching.MemoryCache.MemoryCacheProvider(MemoryCache.Default);var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5));// 设置一个绝对的过期时间var cachePolicy = Policy.Cache(memoryCacheProvider, new AbsoluteTtl(DateTimeOffset.Now.Date.AddDays(1));// 设置一个滑动的过期时间,即每次使用缓存的时候,过期时间会更新

var cachePolicy = Policy.Cache(memoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(5));// 我们用Policy的缓存机制来实现从缓存中读取一个值,如果该值在缓存中不存在则从提供的函数中取出这个值放到缓存中。// 借且于Polly Cache 这个操作只需要一行代码即可。

TResult result = cachePolicy.Execute(() => getFoo(), new Context("FooKey"));
// "FooKey" is the cache key used in this execution.// Define a cache Policy, and catch any cache provider errors for logging.var cachePolicy = Policy.Cache(myCacheProvider, TimeSpan.FromMinutes(5), (context, key, ex) => { logger.Error($"Cache provider, for key {key}, threw exception: {ex}."); // (for example) }
);

组合Policy

最后我们要说的是如何将多个policy组合起来。大致的操作是定义多个policy,然后用Wrap方法即可。

var policyWrap = Policy.Wrap(fallback, cache, retry, breaker, timeout, bulkhead);
policyWrap.Execute(...)

在另一个Policy声明时组合使用其它外部声明的Policy。

PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout);Avatar avatar = Policy.Handle<Whatever>().Fallback<Avatar>(Avatar.Blank).Wrap(commonResilience).Execute(() => { /* get avatar */ });

相关文章:

原文地址:http://www.jessetalk.cn/2018/03/25/asp-vnext-polly-docs/


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

开源服务容错处理库Polly使用文档相关推荐

  1. ASP VNext 开源服务容错处理库Polly

    背景 Polly是一个.NET弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达诸如重试,断路器,超时,隔离头和回退之类的策略. Polly面向.NET Standard 1.1(覆盖范围: ...

  2. 转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档

    在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档 一直以来,我都想为 PDF 补丁丁添加一个 PDF 渲染引擎.可是,目前并没有可以在 .NET 框架上运行的免费 PDF ...

  3. JAVA毕业设计口腔医院患者服务系统计算机源码+lw文档+系统+调试部署+数据库

    JAVA毕业设计口腔医院患者服务系统计算机源码+lw文档+系统+调试部署+数据库 JAVA毕业设计口腔医院患者服务系统计算机源码+lw文档+系统+调试部署+数据库 本源码技术栈: 项目架构:B/S架构 ...

  4. java计算机毕业设计家庭园艺服务平台源码+数据库+lw文档+系统

    java计算机毕业设计家庭园艺服务平台源码+数据库+lw文档+系统 java计算机毕业设计家庭园艺服务平台源码+数据库+lw文档+系统 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 开 ...

  5. JAVA毕业设计家政服务管理系统计算机源码+lw文档+系统+调试部署+数据库

    JAVA毕业设计家政服务管理系统计算机源码+lw文档+系统+调试部署+数据库 JAVA毕业设计家政服务管理系统计算机源码+lw文档+系统+调试部署+数据库 本源码技术栈: 项目架构:B/S架构 开发语 ...

  6. java计算机毕业设计汽车维修服务系统源代码+数据库+系统+lw文档

    java计算机毕业设计汽车维修服务系统源代码+数据库+系统+lw文档 java计算机毕业设计汽车维修服务系统源代码+数据库+系统+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 ...

  7. java计算机毕业设计网上志愿服务系统源代码+数据库+系统+lw文档

    java计算机毕业设计网上志愿服务系统源代码+数据库+系统+lw文档 java计算机毕业设计网上志愿服务系统源代码+数据库+系统+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 ...

  8. java计算机毕业设计旅游服务平台源代码+数据库+系统+lw文档

    java计算机毕业设计旅游服务平台源代码+数据库+系统+lw文档 java计算机毕业设计旅游服务平台源代码+数据库+系统+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 开发软 ...

  9. java计算机毕业设计汽车维修服务系统源码+程序+lw文档+mysql数据库

    java计算机毕业设计汽车维修服务系统源码+程序+lw文档+mysql数据库 java计算机毕业设计汽车维修服务系统源码+程序+lw文档+mysql数据库 本源码技术栈: 项目架构:B/S架构 开发语 ...

最新文章

  1. from __future__ import absolute_import用法心得小结
  2. Python:条件判断
  3. Scala消息通信之akka,akka案例
  4. Vue warn Failed to mount component: template or render function not defined
  5. 作为服务器上的操作系统,作为服务器的操作系统
  6. 跟我极速尝鲜 Spring Boot 2.3
  7. 编程:从前有一个傻呆程序员,老婆交给他一项任务,他办了四次才满意
  8. 使用notepad++作为keil的外部编辑器
  9. 【Vegas原创】ctrl shift无法切换输入法的解决方法
  10. 《麦肯锡方法》第四部分 麦肯锡生存之道 第17-19章-思维导图
  11. Iperf下载及使用说明
  12. OTSU算法实现二值化
  13. 计算机组装所需硬件,电脑组装机配置清单
  14. CSS3 SVG 画一个三角形
  15. vue中使用tinymce富文本
  16. python 赋值语句
  17. 学英语《每日一歌》之she
  18. 钉钉E应用自动登录获取用户信息总结
  19. XP定时关机、查看和取消指令
  20. WM Splitter 2.2.1409.zip

热门文章

  1. Skype for TV停止支持 三星确认今年6月移除该应用
  2. context:annotation-config/,mvc:annotation-driven/和context:component-scan之间的关系
  3. hdoj-1005-Number Sequences
  4. WPF中设置了WindowStyle=None后,窗口仍然有边框的解决方法
  5. uva705--slash maze
  6. js数组去重,合集等操作
  7. 网规:第4章 网络安全-4.5IDS和IPS
  8. 忽略NVRAM的config,修改cisco密码
  9. 错误删除linux分区致Win7引导失败的修复方法
  10. Blazor University (9)组件 — 代码生成 HTML 属性