翻译自:https://github.com/App-vNext/Polly/wiki/PolicyWrap
点此跳转到系列目录
PolicyWrap (v5.0 起)
目标
提供一种简单的方法来组合弹性策略。
概念
PolicyWrap
提供了一种灵活的方式,以嵌套的方式将多个弹性策略应用于委托(有时称为’Russian-Doll’或’onion-skin-layers’模型)(PS:俄罗斯套娃,洋葱皮)。
而不是下面这样的普通写法:
fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));
使用 PolicyWrap
可以像下面这么写:
fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);
或者这样:
fallback.Wrap(waitAndRetry.Wrap(breaker)).Execute(action);
也能这样:
Policy.Wrap(fallback, waitAndRetry, breaker).Execute(action);
所有的 PolicyWrap
实例至少要包含两种策略.
使用
PolicyWrap
执行通过多层次或包装传入的委托:
- 最外面的(读顺序中最左边的)策略执行下一个内部,该内部执行下一个内部,等等;直到最内层的策略执行用户委托;
- 所执行的委托抛出的异常层层向外冒泡(直到得到处理)。
例如, Policy.Wrap(fallback, waitAndRetry, breaker)
的执行图如下:
fallback
是最外层策略; 它通过waitAndRetry
进行调用;waitAndRetry
通过breaker
进行调用;breaker
时最内层策略, 断路器来执行委托(除非断路器断开).- 传入的委托成功执行; 或返回返回值; 或抛出异常.
breaker
将更新该结果的电路统计,然后将其传递回外层…
waitAndRetry
,如果收到的是故障它将等待并重试,或在重试耗尽时失败;如果成功则返回;然后将其传递回外层…
fallback
, 如果收到的是失败,它将替换回退结果;(如果成功)返回它。
语法与用例
实例的语法是同步的; 也有相对应的异步重载方法. 详情参见readme 和 wiki .
实例方法语法
var policyWrap = fallback.Wrap(cache).Wrap(retry).Wrap(breaker).Wrap(bulkhead).Wrap(timeout);
// or (functionally equivalent)
var policyWrap = fallback.Wrap(cache.Wrap(retry.Wrap(breaker.Wrap(bulkhead.Wrap(timeout)))));
策略实例的 .Wrap()
方法的语法是很灵活的: 语法可以混合使用泛型和非泛型策略。编译器可以根据需要推断出PolicyWrap<TResult>
的泛型类型参数。
静态方法语法
非泛型策略包装的静态语法
PolicyWrap policyWrap = Policy.Wrap(fallback, cache, retry, breaker, bulkhead, timeout);
此语法仅将非泛型策略封装在一起。
泛型策略包装的静态语法
PolicyWrap<TResult> genericPolicyWrap = Policy.Wrap<TResult>(fallback, cache, retry, breaker, bulkhead, timeout);
此语法仅将泛型策略封装在一起。
PolicyWrap
特性
PolicyWrap 也是一个策略
PolicyWrap
只是另一个“Policy”,和其他策略具有相同的特性:
- 它是线程安全的 thread-safe
- 也可以多点复用 reused across multiple call sites
- 非泛型的’ PolicyWrap ‘可以跨多个’ TResult ‘类型与泛型的’ . execute /Async(…)'方法一起使用。
函数组合
从函数式编程或数学的角度来看,“PolicyWrap”是一个高阶函数的函数组合,就像Linq或Rx一样。
如果给要执行的委托加上策略看做 f(x)
( f
就是Polly策略 ,x
时要执行的委托), PolicyWrap
则允许你这么使用 a(b(c(d(e(f(x))))))
(或者: a(b(c(d(e(f)))))(x)
), (a
到 f
都是策略, x
是委托).
灵活地重组
“PolicyWrap”的函数组合和线程安全方面产生了一些有趣的特征。
- 同一个
Policy
实例可以安全地以不同的方式在多个PolicyWrap
中使用。(每个“PolicyWrap”可以通过应用程序中配置的策略实例编织不同的执行流程。 在应用程序的策略包中,策略实例不是说一定要有前置或后置策略。) - 一个“PolicyWrap”(策略包)可以进一步与其他策略包组合包装,以构建更强大的组合。
使用组件灵活构筑策略包
实例语法允许你在一个主题上构建变体,组合在一起满足常见的和个性化的弹性需求:
PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout);
// ... 然后和其他策略组合在一起满足特定的功能调用
Avatar avatar = Policy
.Handle<Whatever>()
.Fallback<Avatar>(Avatar.Blank)
.Wrap(commonResilience)
.Execute(() => { /* get avatar */ });
// 共享相同的commonResilience ,但在另一个功能调用点使用不同的回退:
Reputation reps = Policy
.Handle<Whatever>()
.Fallback<Reputation>(Reputation.NotAvailable)
.Wrap(commonResilience)
.Execute(() => { /* get reputation */ });
非泛型 VS 泛型
非泛型策略组成的非泛型
当你把非泛型“策略”打包组合在一起时, 不管有多少个策略, PolicyWrap
仍然是非泛型的. 非泛型策略包也可以执行泛型方法 .Execute<TResult>
。
策略包中有一个是泛型策略则策略包就是泛型的
当你在一个策略包中包含一个泛型的SomePolicy<TResult>
时,PolicyWrap
整体变成了泛型的PolicyWrap<TResult>
。这提供了类型安全。使用改述语法是没有意义的
fallback<int>(...).Execute(() => breaker<string>(...).Execute(() => retry<Foo>(...).Execute<Bar>(func)));
就像Linq和Rx不会让你这么做一样。
但是,您可以[在相同的 PolicyWrap<TResult>
中混合使用泛型和非泛型策略]
(Non-generic-and-generic-policies#mixing-non-generic-and-generic-in-a-policywraptresult), :前提是所有泛型策略都适用于相同的TResult
类型,并且使用实例配置语法。
推荐用法
对可用的策略类型组合进行排序
策略可以按照任何顺序灵活组合。然而,需要考虑如下几点:
Policy type | Common positions in a PolicyWrap | Explanation |
---|---|---|
FallbackPolicy | 通常在最外层 | 其他弹性策略都执行完后仍然返回失败的情况下提供一个替代返回值 |
FallbackPolicy | 也可以放在中间 … | … 例如: 作为调用多个可能端点的故障转移策略(第一次尝试; 如果不行, 下一次尝试). |
CachePolicy | 尽可能的外层(前面),不要和回退策略一起使用 | 尽可能的外置:如果你保存了一个缓存值,你就不想多此一举的尝试隔板或断路器等。 但缓存策略不应该和提供替代值的回退策略一起使用(你肯定不想把回退策略返回的替代值放到缓存中并返回给调用方) |
TimeoutPolicy | 要放在以下策略的外层 RetryPolicy , CircuitBreaker or BulkheadPolicy | … 将总体超时应用于执行,包括尝试之间的延迟或舱壁槽的等待时间 |
RetryPolicy and CircuitBreaker | 重试封装断路器,反之亦然。 | 主观判断. 如果重试之间的延迟较长,我们建议WaitAndRetry封装CircuitBreaker (电路状态可能在两次尝试之间的延迟中发生合理的变化). 如果重试之间没有或很短的延迟,我们建议“CircuitBreaker”封装“重试” (不要以三次间隔很近的重试调用来敲打底层系统作为中断电路的原因). |
BulkheadPolicy | 通常在最里面,除非包装最后一个TimeoutPolicy; 一定要放在WaitAndRetry里面 | 隔板有意限制并行化。 您希望并行化专用于运行委托,而不是被等待重试占用并行资源。 |
TimeoutPolicy | 在 RetryPolicy , CircuitBreaker , BulkheadPolicy 中, 最靠近委托. | …将超时应用于单个尝试。 如果您允许调用排队等待隔墙,并且希望超时他们排队的时间,您也可以在隔墙外面放置一个 TimeoutPolicy 。 |
上面列出的策略的典型排序会产生类似于下面的执行流: |
虽然上面介绍了一种典型的顺序和成熟的策略,但“PolicyWrap”有意地具有完全的灵活性,允许您只包含所需的弹性组件,或者根据需要设计更复杂的策略。
在包装中多次使用同一类型的策略
你可以在一个策略包中多次使用相同的策略类型(例如两次’ RetryPolicy ';两个“FallbackPolicy”)。这对于为不同的故障设置不同的处理策略(作为给定调用点的总体策略的一部分)特别有用。
- 对于一种异常,您可能会比另一种异常重试更多次或延迟更短。
- 对于典型的瞬时故障,您可能有一个重试策略;以及针对特殊情况(如重新身份验证)的单独重试策略;或尊重429 RetryAfter头。
- 可能想要一种例外来立即断开电路;另一些则需要更谨慎地断开。
- 您可以为不同已处理的错误提供不同的回退值/消息。
其他策略类型也可以在wrap中多次使用:
- 您可以对整个尝试应用总体超时(包括waits-between-tries);以及每个单独尝试的单独超时。
- 你可以用不同类型的缓存嵌套多个缓存策略: 例如,由云缓存备份的内存缓存(如果没有使用本质上提供这种双缓存方法的缓存供应器)。
PolicyWrap 搭配 ExecuteAndCapture()
在 PolicyWrap
上使用.ExecuteAndCapture(...)
会捕获最终的执行结果,不管这个结果被最外层的策略视为失败还是成功。
属性
PolicyKey
PolicyWrap
可以像其他策略一样带有 PolicyKey
:
PolicyWrap commonResilience = Policy
.Wrap(retry, breaker, timeout)
.WithPolicyKey("CommonServiceResilience");
策略报的 PolicyKey
是放在执行上下文的 Context
的属性中:
context.PolicyWrapKey
在多重嵌套的策略组合中,附加到最外层换行的PolicyKey
作为context.PolicyWrapKey
贯穿整个执行过程。
未来的Polly路线图设想向Polly添加指标,包括跨“PolicyWrap”或wrap元素的总体执行时间(延迟)指标。
Outer
返回由这个“PolicyWrap”实例表示的这一对的外部策略。
Inner
返回由这个“PolicyWrap”实例表示的这对策略的内部策略。
方法
下面所有的 GetPolicies()
方法从Polly v5.6起可用.
IEnumerable<IsPolicy> GetPolicies()
返回在这个“PolicyWrap”实例中配置的所有策略,按外部>内部顺序。
IEnumerable<TPolicy> GetPolicies<TPolicy>()
返回在这个“PolicyWrap”实例中配置的所有类型为“TPolicy”的策略,按外部>内部顺序。
IEnumerable<TPolicy> GetPolicies<TPolicy>(Func<TPolicy, bool>)
返回在这个’ PolicyWrap ‘实例中匹配过滤器的所有类型为’ TPolicy '的策略,按外>内顺序。
TPolicy GetPolicy<TPolicy>()
返回在这个“PolicyWrap”实例中配置的类型为“TPolicy”的单个策略;如果不匹配,则为’null’。
TPolicy GetPolicy<TPolicy>(Func<TPolicy, bool>)
返回在这个“PolicyWrap”实例中配置的类型为“TPolicy”的符合过滤器的单个策略;如果不匹配,则为’null’。
线程安全与策略复用
线程安全
PolicyWrap
是线程安全的: 多个调用可以通过一个策略实例安全地并发地进行。
策略复用
PolicyWrap
:实例可以跨多个调用站点复用。
在重用策略时,使用“OperationKey”来区分日志和度量中的不同调用站点的使用情况。
翻译自:https://github.com/App-vNext/Polly/wiki/PolicyWrap
点此跳转到系列目录