前言

本节我们来介绍一款强大的库Polly,Polly是一种.NET弹性和瞬态故障处理库,允许我们以非常顺畅和线程安全的方式来执诸如行重试,断路,超时,故障恢复等策略。 Polly针对对.NET 4.0,.NET 4.5和.NET Standard 1.1以及.NET Core实现,该项目作者现已成为.NET基金会一员,项目一直在不停迭代和更新,项目地址【https://github.com/App-vNext/Polly】,你值得拥有。接下来我们以.NET Framework  4.5来演示它的强大功能。

Introduce Polly

首先我们得下载Polly包,最新版本为5.3.1,如下:

该库实现了七种恢复策略,下面我一一为您来介绍。

重试策略(Retry)

重试策略针对的前置条件是短暂的故障延迟且在短暂的延迟之后能够自我纠正。允许我们做的是能够自动配置重试机制。

断路器(Circuit-breaker)

断路器策略针对的前置条件是当系统繁忙时,快速响应失败总比让用户一直等待更好。保护系统故障免受过载,Polly可以帮其恢复。

超时(Timeout)

超时策略针对的前置条件是超过一定的等待时间,想要得到成功的结果是不可能的,保证调用者不必等待超时。

隔板隔离(Bulkhead Isolation)

隔板隔离针对的前置条件是当进程出现故障时,多个失败一直在主机中对资源(例如线程/ CPU)一直占用。下游系统故障也可能导致上游失败。这两个风险都将造成严重的后果。都说一粒老鼠子屎搅浑一锅粥,而Polly则将受管制的操作限制在固定的资源池中,免其他资源受其影响。

缓存(Cache)

缓存策略针对的前置条件是数据不会很频繁的进行更新,为了避免系统过载,首次加载数据时将响应数据进行缓存,如果缓存中存在则直接从缓存中读取。

回退(Fallback)

操作仍然会失败,也就是说当发生这样的事情时我们打算做什么。也就是说定义失败返回操作。

策略包装(PolicyWrap)

策略包装针对的前置条件是不同的故障需要不同的策略,也就意味着弹性灵活使用组合。

几种策略使用

一旦从事IT就得警惕异常并友好拥抱异常而非不闻不问,这个时候我们利用try{}catch{}来处理。

            try
{
var a = ;
var b = / a;
}
catch (DivideByZeroException ex)
{ throw ex;
}

若我们想重试三次,此时我们只能进行循环三次操作。我们只能简单进行处理,自从有了Polly,什么重试机制,超时都不在话下,下面我们来简短介绍各种策略。Polly默认处理策略需要指定抛出的具体异常或者执行抛出异常返回的结果。处理单个类型异常如下:

Policy
.Handle<DivideByZeroException>()

上述异常指尝试除以0,下面我们演示下具体使用,我们尝试除以0并用Polly指定该异常并重试三次。

        static int Compute()
{
var a = ;
return / a;
}
            try
{
var retryTwoTimesPolicy =
Policy
.Handle<DivideByZeroException>()
.Retry(, (ex, count) =>
{
Console.WriteLine("执行失败! 重试次数 {0}", count);
Console.WriteLine("异常来自 {0}", ex.GetType().Name);
});
retryTwoTimesPolicy.Execute(() =>
{
Compute();
});
}
catch (DivideByZeroException e)
{
Console.WriteLine($"Excuted Failed,Message: ({e.Message})"); }

如果我们想指定处理多个异常类型通过OR即可。

Policy
.Handle<DivideByZeroException>()
.Or<ArgumentException>()

当然还有更加强大的功能,比如在微信支付时,微信回调我们的应用程序时,此时若失败,想必微信那边也会做重试机制,例如隔一段时间重试调用一次,重复调用几次后仍失败则不再回调。我们利用Polly则可以演示等待重试机制。

        /// <summary>
/// 抛出异常
/// </summary>
static void ZeroExcepcion()
{
throw new DivideByZeroException();
}
        /// <summary>
/// 异常信息
/// </summary>
/// <param name="e"></param>
/// <param name="tiempo"></param>
/// <param name="intento"></param>
/// <param name="contexto"></param>
static void ReportaError(Exception e, TimeSpan tiempo, int intento, Context contexto)
{
Console.WriteLine($"异常: {intento:00} (调用秒数: {tiempo.Seconds} 秒)\t执行时间: {DateTime.Now}");
}
            try
{
var politicaWaitAndRetry = Policy
.Handle<DivideByZeroException>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(),
TimeSpan.FromSeconds(),
TimeSpan.FromSeconds(),
TimeSpan.FromSeconds()
}, ReportaError);
politicaWaitAndRetry.Execute(() =>
{
ZeroExcepcion();
});
}
catch (Exception e)
{
Console.WriteLine($"Executed Failed,Message:({e.Message})");
}

我们讲完默认策略和重试策略,再来看看反馈策略,翻译的更通俗一点则是执行失败后返回的结果,此时要为Polly指定返回类型,然后指定异常,最后调用Fallback方法。

        static string ThrowException()
{
throw new Exception();
}
           var fallBackPolicy =
Policy<string>
.Handle<Exception>()
.Fallback("执行失败,返回Fallback"); var fallBack = fallBackPolicy.Execute(() =>
{
return ThrowException();
});
Console.WriteLine(fallBack);

包裹策略说到底就是混合多种策略,并执行。

          var fallBackPolicy =
Policy<string>
.Handle<Exception>()
.Fallback("执行失败,返回Fallback"); var fallBack = fallBackPolicy.Execute(() =>
{
return ThrowException();
});
Console.WriteLine(fallBack); var politicaWaitAndRetry =
Policy<string>
.Handle<Exception>()
.Retry(, (ex, count) =>
{
Console.WriteLine("执行失败! 重试次数 {0}", count);
Console.WriteLine("异常来自 {0}", ex.GetType().Name);
}); var mixedPolicy = Policy.Wrap(fallBackPolicy, politicaWaitAndRetry);
var mixedResult = mixedPolicy.Execute(ThrowException);
Console.WriteLine($"执行结果: {mixedResult}");

至此关于Polly的基本介绍就已结束,该库还是非常强大,更多特性请参考上述github例子,接下来我们来看看两种具体场景。

ASP.NET Web APi使用Polly重试机制

在Polly v4.30中以上可以利用HandleResult指定返回结果,如下:

Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)

基于此我们完全可以利用执行Web APi中的响应策略,如下:

 public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;

拿到响应中状态码,若为500则重试三次。

 _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
r => r.StatusCode == HttpStatusCode.InternalServerError)
.WaitAndRetryAsync(,
retryAttempt => TimeSpan.FromSeconds(retryAttempt));

上述获取请求响应策略在构造函数中获取。

    public class PollyController : ApiController
{
public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;
public PollyController()
{
_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
r => r.StatusCode == HttpStatusCode.InternalServerError)
.WaitAndRetryAsync(,
retryAttempt => TimeSpan.FromSeconds(retryAttempt));
}
}

此时调用接口时执行策略的Execute或者ExecuteAsync方法即可。

        public async Task<IHttpActionResult> Get()
{
var httpClient = new HttpClient();
string requestEndpoint = "http://localhost:4096"; HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint)); IEnumerable<string> numbers = await httpResponse.Content.ReadAsAsync<IEnumerable<string>>(); return Ok(numbers);
}

你以为仅限于在Web APi中使用吗?在其他框架中也可以使用,例如EntityFramework 6.x中,在EntityFramework 6+上出现了执行策略,也就是执行重试机制,这个时候我们依然可以借助Polly轮子来实现。

EntityFramework 6.x使用Polly重试机制

在EntityFramework 6.x中有如下执行策略接口,看起来是不是和Polly中的Execute方法是不是很类似。

    //
// 摘要:
// A strategy that is used to execute a command or query against the database, possibly
// with logic to retry when a failure occurs.
public interface IDbExecutionStrategy
{
//
// 摘要:
// Indicates whether this System.Data.Entity.Infrastructure.IDbExecutionStrategy
// might retry the execution after a failure.
bool RetriesOnFailure { get; } //
// 摘要:
// Executes the specified operation.
//
// 参数:
// operation:
// A delegate representing an executable operation that doesn't return any results.
void Execute(Action operation);
//
// 摘要:
// Executes the specified operation and returns the result.
//
// 参数:
// operation:
// A delegate representing an executable operation that returns the result of type
// TResult.
//
// 类型参数:
// TResult:
// The return type of operation.
//
// 返回结果:
// The result from the operation.
TResult Execute<TResult>(Func<TResult> operation);
//
// 摘要:
// Executes the specified asynchronous operation.
//
// 参数:
// operation:
// A function that returns a started task.
//
// cancellationToken:
// A cancellation token used to cancel the retry operation, but not operations that
// are already in flight or that already completed successfully.
//
// 返回结果:
// A task that will run to completion if the original task completes successfully
// (either the first time or after retrying transient failures). If the task fails
// with a non-transient error or the retry limit is reached, the returned task will
// become faulted and the exception must be observed.
Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken);
//
// 摘要:
// Executes the specified asynchronous operation and returns the result.
//
// 参数:
// operation:
// A function that returns a started task of type TResult.
//
// cancellationToken:
// A cancellation token used to cancel the retry operation, but not operations that
// are already in flight or that already completed successfully.
//
// 类型参数:
// TResult:
// The result type of the System.Threading.Tasks.Task`1 returned by operation.
//
// 返回结果:
// A task that will run to completion if the original task completes successfully
// (either the first time or after retrying transient failures). If the task fails
// with a non-transient error or the retry limit is reached, the returned task will
// become faulted and the exception must be observed.
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken);
}

EntityFramework 6.x中的执行策略说到底就是数据库连接问题即弹性连接,若考虑到数据库过渡负载问题,此时应用程序和数据库之间存在网络问题的话。可能数据库连接在几秒内才返回,此时也没有什么很大的问题,我们完全可以再尝试一次,此时或许过了连接频繁期,保证连接立马恢复。如果数据库连接一会恢复不了呢?或许是五分钟,又或者是半个小时。如果我们只是一味盲目的进行重试,这显然不可取。如果我们的应用程序连接超时时间超过了20秒,若我们选择继续连接到数据库,我们将很快用完我们应用程序池中的工作线程。一直等待数据库的响应。此时网站将完全无响应,同时会给用户页面无响应的友好提醒。这是Polly库中描述断路器的很好例子,换句话说如果我们捕获了m个数量的SqlExceptions,假设数据库有其他问题存在,导致我们不能在n秒内再尝试连接数据库。此时在数据库连接上存在一个问题,那就是阻塞了我们的应用程序工作线程被挂起,我们试图连接数据库,我们假设不可用的话,但是我们要打破这种不可用,那就用Polly吧。

我们看到上述EntityFramework 6.x实现了IDbExecutionStrategy接口,但没有实现如Polly中的断路器模式,EntityFramework 6.x中的执行策略只是重试机制而已。 比如SqlAzureExecutionStrategy将在指定的时间段内重试指定的次数,直到一段时间段过去,重试指数过后,接着就是失败。 同时所有后续调用将执行相同操作,重试并失败。 这是调用数据库时最好的策略吗? 不敢肯定,或许Polly中的断路器模式值得我们借鉴。我们自己来实现上述执行策略接口。

    public class CirtuitBreakerExecutionStrategy : IDbExecutionStrategy
{
private Policy _policy; public CirtuitBreakerExecutionStrategy(Policy policy)
{
_policy = policy;
} public void Execute(Action operation)
{ _policy.Execute(() =>
{
operation.Invoke();
});
} public TResult Execute<TResult>(Func<TResult> operation)
{
return _policy.Execute(() =>
{
return operation.Invoke();
});
} public async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken)
{
await _policy.ExecuteAsync(() =>
{
return operation.Invoke();
});
} public async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken)
{
return await _policy.ExecuteAsync(() =>
{
return operation.Invoke();
});
} public bool RetriesOnFailure { get { return true; } }
}

接下来在基于代码配置文件中设置我们上述自定义实现的断路器模式。

    public class EFConfiguration : DbConfiguration
{
public Policy _policy;
public EFConfiguration()
{
_policy = Policy.Handle<Exception>().CircuitBreaker(, TimeSpan.FromSeconds()); SetExecutionStrategy("System.Data.SqlClient", () => new CirtuitBreakerExecutionStrategy(_policy));
}
}

上述自定义实现执行策略不保证一定有用或许也是一种解决方案呢。

总结

本节我们介绍了强大的Polly库和其对应使用的两种实际场景,有此轮子我们何不用起,将其进行封装可以用于一切重试、缓存、异常等处理。

我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。

已被.NET基金会认可的弹性和瞬态故障处理库Polly介绍的更多相关文章

  1. NET Core微服务之路:弹性和瞬态故障处理库Polly的介绍

    前言 上一节中我们介绍了Ocelot的常见使用配置,通过json配置文件,实现API网关的请求处理.和一个使用DownStream扩展下游中间件,来实现Http转RPC的简单实现,功能不算强大,但可以 ...

  2. 弹性和瞬态故障处理库Polly

    介绍 本节我们来介绍一款强大的库Polly,Polly是一种.NET弹性和瞬态故障处理库,允许我们以非常顺畅和线程安全的方式来执诸如行重试,断路,超时,故障恢复等策略. Polly针对对.NET 4. ...

  3. Polly一种.NET弹性和瞬态故障处理库(重试策略、断路器、超时、隔板隔离、缓存、回退、策略包装)

    下载地址:https://github.com/App-vNext/Polly 该库实现了七种恢复策略. 重试策略(Retry) 重试策略针对的前置条件是短暂的故障延迟且在短暂的延迟之后能够自我纠正. ...

  4. .NET的弹性及瞬间错误处理库Polly

    原文:.NET的弹性及瞬间错误处理库Polly 本文基本是官方说明的翻译和总结(https://github.com/App-vNext/Polly) 什么是Polly? Polly是一款基于.NET ...

  5. 基于.NET的弹性及瞬间错误处理库Polly

    本文基本是官方说明的翻译和总结(https://github.com/App-vNext/Polly) 什么是Polly? Polly是一款基于.NET的弹性及瞬间错误处理库, 它允许开发人员以顺畅及 ...

  6. k3 cloud成本调整单引入单据后,再做出库成本核算。成本调整单列表已审核的单据消失,非已审核的单据还在,这是出库成本核算设置参数的问题吗?

    存货核算时,会将“期末余额调整”类型的的调整单删除后,再重新产生:因此引入后不要再做出库核算,或者引入其它类型的单据.

  7. Asp.Net Core API网关Ocelot

    首先,让我们简单了解下什么是API网关? API网关是一个服务器,是系统的唯一入口.从面向对象设计的角度看,它与外观模式类似.API网关封装了系统内部架构,为每个客户端提供一个定制的API.它可能还具 ...

  8. .NET Core微服务之基于Polly+AspectCore实现熔断与降级机制

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.熔断.降级与AOP 1.1 啥是熔断? 在广义的解释中,熔断主要是指为控制股票.期货或其他金融衍生产品的交易风险,为其单日价格波动幅度 ...

  9. Net Core API网关Ocelot

    Ocelot在github的地址 https://github.com/TomPallister/Ocelot , 非常给力的是在课程当天完成了.NET Core 2.0的升级,升级过程请看https ...

随机推荐

  1. (转载)2016 CCF大数据与计算智能大赛 开源资料整理

    本文转载自:http://blog.sina.com.cn/s/blog_5399b8660102wxks.html 2016 CCF 大数据与计算智能大赛已经落下帷幕,11个赛题由众多大神包揽奖项, ...

  2. js获取ip地址,操作系统,浏览器版本等信息,可兼容

    这次呢,说一下使用js获取用户电脑的ip信息,刚开始只是想获取用户ip,后来就顺带着获取了操作系统和浏览器信息. 先说下获取用户ip地址,包括像ipv4,ipv6,掩码等内容,但是大部分都要根据浏览器 ...

  3. 面试技巧,如何通过索引说数据库优化能力,内容来自Java web轻量级开发面试教程

    上星期写了一个篇文章,数据库方面的面试技巧,如何从建表方面展示自己能力,承蒙管理员抬举,放入首页,也承蒙各位厚爱,两天内收获了将近770个点击,也一度进入48小时热榜. 为了感谢管理员和大家的支持,再 ...

  4. 数据结构之合并链表STL

    #include <iostream> #include <list> using namespace std; int main() { int n, m; while (c ...

  5. python进阶学习(二)

    本节学习图形用户界面 ------------------------ 本节介绍如何创建python程序的图形用户界面(GUI),也就是那些带有按钮和文本框的窗口.这里介绍wxPython : 下载地 ...

  6. java面向对象(一)

    [toc] 面向对象 我们都说java是面向对象的编程语言,那什么是面向对象呢?什么是类呢?什么是方法呢? 类.对象.方法 类是对象的抽象定义,对象是类的具体实例. 类:指的是一类东西,比如汽车,人类 ...

  7. Springmvc_validation 效验器

    springmvc-validation效验器的使用介绍 对于任何一个应用来说,都会做数据的有效性效验,但是只在前端做并不是很安全,考虑到安全性這个时候会要求我们在服务端也对数据进行有效验证,spri ...

  8. 如何通过navigator.userAgent判断是哪款浏览器?

    userAgent 用户代理.通过浏览器控制台alert( navigator.userAgent );可以获得当前浏览器的信息,如果逆推呢? 通过navigator.userAgent判断是哪款浏览 ...

  9. 蓝桥杯PREV-11:横向打印二叉树

    嗯,没错我还报了蓝桥杯. 这是题目 问题描述 二叉树可以用于排序.其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树. 当遇到空子树时,则把该节点放 ...

  10. 团队作业4——第一次项目冲刺(Alpha版本)第一天 and 第二天

    第一天冲刺 一.Daily Scrum Meeting照片 二.每个人的工作 1.今天计划完成的任务 徐璨 申悦:查找关于安卓开发资料,环境搭建 连永刚 林方言:设计项目所要实现的功能,并对功能进行详 ...