服务容错处理库Polly使用
服务容错处理库Polly使用
在进入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, // 408
HttpStatusCode.InternalServerError, // 500
HttpStatusCode.BadGateway, // 502
HttpStatusCode.ServiceUnavailable, // 503
HttpStatusCode.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()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan, context) => {
// do something
});
把WiatAndRetry抱成WaitAndRetryForever()则可以实现重试直到成功。
熔断
熔断也可以被作为当遇到某种错误场景下的一个操作。以下代码展示了当发生2次SomeExceptionType的异常的时候则会熔断1分钟,该操作后续如果继续尝试执行则会直接返回错误 。
Policy
.Handle()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
可以在熔断和恢复的时候定义委托来做一些额外的处理。onBreak会在被熔断时执行,而onReset则会在恢复时执行。
熔断器状态
我们的CircuitBreakPolicy的State定义了当前熔断器的状态,我们也可能调用它的Is
Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
Action onReset = () => { ... };
CircuitBreakerPolicy breaker = Policy
.Handle()
.CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);
olate和Reset方法来手动熔断和恢复 。
CircuitState state = breaker.CircuitState;
Closed 关闭状态,允许执行
Open 自动打开,执行会被阻断
Isolate 手动打开,执行会被阻断
HalfOpen 从自动打开状态恢复中,在熔断时间到了之后从Open状态切换到Closed
// 手动打开熔断器,阻止执行
breaker.Isolate();
// 恢复操作,启动执行
breaker.Reset();
回退(Fallback)
// 如果执行失败则返回UserAvatar.Blank
Policy
.Handle()
.Fallback(UserAvatar.Blank)
// 发起另外一个请求去获取值
Policy
.Handle()
.Fallback(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }
// 返回一个指定的值,添加额外的处理操作。onFallback
Policy
.Handle()
.Fallback(UserAvatar.Blank, onFallback: (exception, context) =>
{
// do something
});
执行polly policy
为我声明了一个Policy,并定义了它的异常条件和处理方式,那么接下来就是执行它。执行是把我们具体要运行的代码放到Policy里面。
// 执行一个Action
var policy = Policy
.Handle()
.Retry();
policy.Execute(() => DoSomething());
这就是我们最开始的例子,还记得我们在异常处理的时候有一个context上下文吗?我们可以在执行的时候带一些参数进去
// 看我们在retry重试时被调用的一个委托,它可以从context中拿到我们在execute的时候传进来的参数 。
var policy = Policy
.Handle()
.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(ex => ex.Number == 1205)
.Or(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的时候,它会通知执行线程取消并终止执行线程,避免额外的开销。下面的乐观超时的具体用法 。
// 声明 Policy
Policy 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()
.Fallback(Avatar.Blank)
.Wrap(commonResilience)
.Execute(() => { /* get avatar */ });
写在后面
上一篇我们介绍了《asp.net core开源api 网关Ocelot的中文使用文档》,Ocelot里面的一些关于Qos服务质量的处理就是用Polly来实现的。当然在没有网关介入的情况 下,我们也可以单独来使用Polly做弹性应对和瞬时错误处理。关于分布式架构,这是一个很大的话题,我们后面继续展示,欢迎关注 。
作者:Jesse 出处: http://jesse2013.cnblogs.com/
服务容错处理库Polly使用的更多相关文章
- ASP VNext 开源服务容错处理库Polly使用文档
在进入SOA之后,我们的代码从本地方法调用变成了跨机器的通信.任何一个新技术的引入都会为我们解决特定的问题,都会带来一些新的问题.比如网络故障.依赖服务崩溃.超时.服务器内存与CPU等其它问题.正是因 ...
- 容错处理库Polly使用文档
Design For Failure1. 一个依赖服务的故障不会严重破坏用户的体验.2. 系统能自动或半自动处理故障,具备自我恢复能力. 以下是一些经验的服务容错模式 超时与重试(Timeout an ...
- NET Core微服务之路:弹性和瞬态故障处理库Polly的介绍
前言 上一节中我们介绍了Ocelot的常见使用配置,通过json配置文件,实现API网关的请求处理.和一个使用DownStream扩展下游中间件,来实现Http转RPC的简单实现,功能不算强大,但可以 ...
- 第五章 服务容错保护:Spring Cloud Hystrix
在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能 ...
- Dubbo服务容错(整合hystrix)
简介:Hystrix旨在通过控制那些访问远程系统.服务和第三方库的节点从而对延迟和故障提供更强大的容错能力,Hystrix具备拥有回退机制和断路器功能的线程和信号隔离.请求缓存和请求打包以及监控和配置 ...
- 服务容错保护断路器Hystrix之七:做到自动降级
从<高可用服务设计之二:Rate limiting 限流与降级>中的“自动降级”中,我们这边将系统遇到“危险”时采取的整套应急方案和措施统一称为降级或服务降级.想要帮助服务做到自动降级,需 ...
- 服务容错保护断路器Hystrix之三:断路器监控(Hystrix Dashboard)-单体监控
turbine:英 [ˈtɜ:baɪn] 美 [ˈtɜ:rbaɪn] n.汽轮机;涡轮机;透平机 一.Hystrix Dashboard简介 在微服务架构中为了保证程序的可用性,防止程序出错导致网络阻 ...
- 服务容错保护断路器Hystrix之二:Hystrix工作流程解析
一.总运行流程 当你发出请求后,hystrix是这么运行的 红圈 :Hystrix 命令执行失败,执行回退逻辑.也就是大家经常在文章中看到的“服务降级”. 绿圈 :四种情况会触发失败回退逻辑( fal ...
- 服务容错保护断路器Hystrix之一:入门示例介绍(springcloud引入Hystrix的两种方式)
限流知识<高可用服务设计之二:Rate limiting 限流与降级> 在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的 ...
随机推荐
- emacs窗口半透明
转自 http://blog.csdn.net/dsjlzh/article/details/7804733 ;; transform window;; Anchor: March Liu (刘鑫) ...
- Serverless+SCF=打倒服务器,解放程序员
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙 发表于云+社区专栏 "你做什么工作的?" "程序员." "那正好, ...
- Mybatis基本介绍
Mybatis介绍 1.Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了goog ...
- WAMP环境配置-PHP安装
我这次环境配置安装的是php-5.6.25版本! (最近我在反复安装PHP的时候出现了一个问题,httpd.conf加载php5apache2_4.dll出现错误,怎么修改都不行,此时我安装的是VC1 ...
- 标准webservice调用
现代定义的webservice一般都倾向于restfull风格的http请求,但工作中还是会遇到前辈们写的时代代码. 我们更倾向于封装代码来调用,而不是服务引用.请看: Service.asmx服务的 ...
- out参数
out参数: 参数在方法的内部必须为其赋值:可以同时返回不同类型的值: 在Main方法里定义,在方法里赋值: 输 ...
- PowerShell 惠普打印机双面驱动自动设置已安装
win10系统,使用实验室的HP P2055dn打印机.每次关机重连后,都会把默认的双面打印机的设置改回“未安装”,需要手动改成“已安装”.感觉是个bug,win7的时候关机后状态还会保持. 每次连上 ...
- 1.Windows服务-->添加一个简单的服务
Windows服务应用程序是一种需要长期运行的应用程序,它对于服务器环境特别适合.它没有用户界面,并且也不会产生任何可视输出.任何用户消息都会被 写进Windows事件日志.计算机启动时,服务会自动开 ...
- javaweb之MVC设计模式
1.MVC简介 MVC是Model-View-Controller的简称,即模型-视图-控制器.MVC是一种设计模式,它把应用程序分成三个核心模块:模型,视图,控制器,它们各自处理自己的任务. 模型( ...
- Spring_Spring的特点
一.非侵入式编程 Spring框架的API不会再业务逻辑上出现,即业务逻辑是POJO(Plain Ordinary Java Object).由于业务逻辑中没有Spring的API,所以业务逻辑可以从 ...