弹性和瞬态故障处理库Polly介绍
前言
本節(jié)我們來介紹一款強(qiáng)大的庫Polly,Polly是一種.NET彈性和瞬態(tài)故障處理庫,允許我們以非常順暢和線程安全的方式來執(zhí)諸如行重試,斷路,超時(shí),故障恢復(fù)等策略。 Polly針對(duì)對(duì).NET 4.0,.NET 4.5和.NET Standard 1.1以及.NET Core實(shí)現(xiàn),該項(xiàng)目作者現(xiàn)已成為.NET基金會(huì)一員,項(xiàng)目一直在不停迭代和更新,項(xiàng)目地址【https://github.com/App-vNext/Polly】,你值得擁有。接下來我們以.NET Framework ?4.5來演示它的強(qiáng)大功能。
Introduce Polly
我們在下載Polly包,最新版本為5.3.1,如下:
該庫實(shí)現(xiàn)了七種恢復(fù)策略,下面我一一為您來介紹。
重試策略(Retry)
重試策略針對(duì)的前置條件是短暫的故障延遲且在短暫的延遲之后能夠自我糾正。允許我們做的是能夠自動(dòng)配置重試機(jī)制。
斷路器(Circuit-breaker)
斷路器策略針對(duì)的前置條件是當(dāng)系統(tǒng)嚴(yán)重掙扎時(shí),快速失敗比讓用戶/呼叫者等待更好。保護(hù)故障系統(tǒng)免受過載,可以幫助它恢復(fù)。
超時(shí)(Timeout)
超時(shí)策略針對(duì)的前置條件是超過一定的等待時(shí)間,想要得到成功的結(jié)果是不可能的,保證調(diào)用者不必等待超時(shí)。
隔板隔離(Bulkhead Isolation)
隔板隔離針對(duì)的前置條件是當(dāng)進(jìn)程出現(xiàn)故障時(shí),備份的多個(gè)失敗的呼叫可以輕松地在主機(jī)中對(duì)資源(例如線程/ CPU)進(jìn)行漫游。下游系統(tǒng)故障也可能導(dǎo)致上游“備份”失敗的呼叫。這兩個(gè)風(fēng)險(xiǎn)都是一個(gè)錯(cuò)誤的過程,導(dǎo)致更廣泛的系統(tǒng)。
緩存(Cache)
緩存策略針對(duì)的前置條件是數(shù)據(jù)不會(huì)很頻繁的進(jìn)行更新,為了避免系統(tǒng)過載,首次加載數(shù)據(jù)時(shí)將響應(yīng)數(shù)據(jù)進(jìn)行緩存,如果緩存中存在則直接從緩存中讀取。
反饋(Fallback)
操作仍然會(huì)失敗,也就是說當(dāng)發(fā)生這樣的事情時(shí)我們打算做什么。也就是說定義失敗返回操作。
策略包裝(PolicyWrap)
策略包裝針對(duì)的前置條件是不同的故障需要不同的策略,也就意味著彈性靈活使用組合。
幾種策略使用
一旦從事IT就得警惕異常并友好擁抱異常而非不聞不問,這個(gè)時(shí)候我們利用try{}catch{}來處理。
try{ ? ? ? ? ? ? ? ?var a = 0; ? ? ? ? ? ? ? ?var b = 1 / a;} ? ? ? ? ? ?catch (DivideByZeroException ex){ ? ? ? ? ? ? ? ?throw ex;}若我們想重試三次,此時(shí)我們只能進(jìn)行循環(huán)三次操作。我們只能簡單進(jìn)行處理,自從有了Polly,什么重試機(jī)制,超時(shí)都不在話下,下面我們來簡短介紹各種策略。Polly默認(rèn)處理策略需要指定拋出的具體異常或者執(zhí)行拋出異常返回的結(jié)果。處理單個(gè)類型異常如下:
Policy.Handle<DivideByZeroException>()上述異常指嘗試除以0,下面我們演示下具體使用,我們嘗試除以0并用Polly指定該異常并重試三次。
static int Compute(){ ? ? ? ? ? ?var a = 0; ? ? ? ?? ? ? ? ? ? ? ? ?? ?return 1 / a;}
如果我們想指定處理多個(gè)異常類型通過OR即可。
Policy.Handle<DivideByZeroException>().Or<ArgumentException>()當(dāng)然還有更加強(qiáng)大的功能,比如在微信支付時(shí),微信回調(diào)我們的應(yīng)用程序時(shí),此時(shí)若失敗,想必微信那邊也會(huì)做重試機(jī)制,例如隔一段時(shí)間重試調(diào)用一次,重復(fù)調(diào)用幾次后仍失敗則不再回調(diào)。我們利用Polly則可以演示等待重試機(jī)制。
/// <summary>/// 拋出異常 ? ? ? ?/// </summary>static void ZeroExcepcion(){ ? ? ? ? ? ?throw new DivideByZeroException();}我們講完默認(rèn)策略和重試策略,再來看看反饋策略,翻譯的更通俗一點(diǎn)則是執(zhí)行失敗后返回的結(jié)果,此時(shí)要為Polly指定返回類型,然后指定異常,最后調(diào)用Fallback方法。
static string ThrowException(){ ? ? ? ? ? ?throw new Exception();}包裹策略說到底就是混合多種策略,并執(zhí)行。
var fallBackPolicy =Policy<string>.Handle<Exception>().Fallback("執(zhí)行失敗,返回Fallback"); ? ? ? ? ? ?var fallBack = fallBackPolicy.Execute(() =>{ ? ? ? ? ? ? ? ?return ThrowException();});Console.WriteLine(fallBack); ? ? ??? ? ?var politicaWaitAndRetry = Policy<string>.Handle<Exception>().Retry(3, (ex, count) =>{Console.WriteLine("執(zhí)行失敗! 重試次數(shù) {0}", count);Console.WriteLine("異常來自 {0}", ex.GetType().Name);}); ? ? ? ?
? ?var mixedPolicy = Policy.Wrap(fallBackPolicy, politicaWaitAndRetry); ? ? ? ?
? ?var mixedResult = mixedPolicy.Execute(ThrowException);Console.WriteLine($"執(zhí)行結(jié)果: {mixedResult}");
至此關(guān)于Polly的基本介紹就已結(jié)束,該庫還是非常強(qiáng)大,更多特性請(qǐng)參考上述github例子,接下來我們來看看兩種具體場景。
ASP.NET Web APi使用Polly重試機(jī)制
在Polly v4.30中以上可以利用HandleResult指定返回結(jié)果,如下:
Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)基于此我們完全可以利用執(zhí)行Web APi中的響應(yīng)策略,如下:
public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;拿到響應(yīng)中狀態(tài)碼,若為500則重試三次。
_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError).WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(retryAttempt));上述獲取請(qǐng)求響應(yīng)策略在構(gòu)造函數(shù)中獲取。
public class PollyController : ApiController{ ? ? ?? ? ??public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy; ? ? ?
? ? ? ?public PollyController(){_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError).WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(retryAttempt));}}
此時(shí)調(diào)用接口時(shí)執(zhí)行策略的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+上出現(xiàn)了執(zhí)行策略,也就是執(zhí)行重試機(jī)制,這個(gè)時(shí)候我們依然可以借助Polly輪子來實(shí)現(xiàn)。
EntityFramework 6.x使用Polly重試機(jī)制
在EntityFramework 6.x中有如下執(zhí)行策略接口,看起來是不是和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.
? ? ? ? //
? ? ? ? // 參數(shù):
? ? ? ? //? ?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.
? ? ? ? //
? ? ? ? // 參數(shù):
? ? ? ? //? ?operation:
? ? ? ? //? ? ?A delegate representing an executable operation that returns the result of type
? ? ? ? //? ? ?TResult.
? ? ? ? //
? ? ? ? // 類型參數(shù):
? ? ? ? //? ?TResult:
? ? ? ? //? ? ?The return type of operation.
? ? ? ? //
? ? ? ? // 返回結(jié)果:
? ? ? ? //? ? ?The result from the operation.
? ? ? ? TResult Execute<TResult>(Func<TResult> operation);
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Executes the specified asynchronous operation.
? ? ? ? //
? ? ? ? // 參數(shù):
? ? ? ? //? ?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.
? ? ? ? //
? ? ? ? // 返回結(jié)果:
? ? ? ? //? ? ?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.
? ? ? ? //
? ? ? ? // 參數(shù):
? ? ? ? //? ?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.
? ? ? ? //
? ? ? ? // 類型參數(shù):
? ? ? ? //? ?TResult:
? ? ? ? //? ? ?The result type of the System.Threading.Tasks.Task`1 returned by operation.
? ? ? ? //
? ? ? ? // 返回結(jié)果:
? ? ? ? //? ? ?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中的執(zhí)行策略說到底就是數(shù)據(jù)庫連接問題即彈性連接,若考慮到數(shù)據(jù)庫過渡負(fù)載問題,此時(shí)應(yīng)用程序和數(shù)據(jù)庫之間存在網(wǎng)絡(luò)問題的話。可能數(shù)據(jù)庫連接在幾秒內(nèi)才返回,此時(shí)也沒有什么很大的問題,我們完全可以再嘗試一次,此時(shí)或許過了連接頻繁期,保證連接立馬恢復(fù)。如果數(shù)據(jù)庫連接一會(huì)恢復(fù)不了呢?或許是五分鐘,又或者是半個(gè)小時(shí)。如果我們只是一味盲目的進(jìn)行重試,這顯然不可取。如果我們的應(yīng)用程序連接超時(shí)時(shí)間超過了20秒,若我們選擇繼續(xù)連接到數(shù)據(jù)庫,我們將很快用完我們應(yīng)用程序池中的工作線程。一直等待數(shù)據(jù)庫的響應(yīng)。此時(shí)網(wǎng)站將完全無響應(yīng),同時(shí)會(huì)給用戶頁面無響應(yīng)的友好提醒。這是Polly庫中描述斷路器的很好例子,換句話說如果我們捕獲了m個(gè)數(shù)量的SqlExceptions,假設(shè)數(shù)據(jù)庫有其他問題存在,導(dǎo)致我們不能在n秒內(nèi)再嘗試連接數(shù)據(jù)庫。此時(shí)在數(shù)據(jù)庫連接上存在一個(gè)問題,那就是阻塞了我們的應(yīng)用程序工作線程被掛起,我們試圖連接數(shù)據(jù)庫,我們假設(shè)不可用的話,但是我們要打破這種不可用,那就用Polly吧。
?
我們看到上述EntityFramework 6.x實(shí)現(xiàn)了IDbExecutionStrategy接口,但沒有實(shí)現(xiàn)如Polly中的斷路器模式,EntityFramework 6.x中的執(zhí)行策略只是重試機(jī)制而已。 比如SqlAzureExecutionStrategy將在指定的時(shí)間段內(nèi)重試指定的次數(shù),直到一段時(shí)間段過去,重試指數(shù)過后,接著就是失敗。 同時(shí)所有后續(xù)調(diào)用將執(zhí)行相同操作,重試并失敗。 這是調(diào)用數(shù)據(jù)庫時(shí)最好的策略嗎? 不敢肯定,或許Polly中的斷路器模式值得我們借鑒。我們自己來實(shí)現(xiàn)上述執(zhí)行策略接口。
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; } }}
接下來在基于代碼配置文件中設(shè)置我們上述自定義實(shí)現(xiàn)的斷路器模式。
? ?public class EFConfiguration : DbConfiguration{ ? ?? ? ? ?public Policy _policy; ? ?
? ?
? ? ? ?public EFConfiguration(){_policy = Policy.Handle<Exception>().CircuitBreaker(3, TimeSpan.FromSeconds(60));SetExecutionStrategy("System.Data.SqlClient", () => new CirtuitBreakerExecutionStrategy(_policy));}}
上述自定義實(shí)現(xiàn)執(zhí)行策略不保證一定有用或許也是一種解決方案呢。
總結(jié)
本節(jié)我們介紹了強(qiáng)大的Polly庫和其對(duì)應(yīng)使用的兩種實(shí)際場景,有此輪子我們何不用起,將其進(jìn)行封裝可以用于一切重試、緩存、異常處理。?
相關(guān)文章:?
云計(jì)算設(shè)計(jì)模式(一)緩存預(yù)留模式
使用熔斷器設(shè)計(jì)模式保護(hù)軟件
云計(jì)算設(shè)計(jì)模式(二)——斷路器模式
API網(wǎng)關(guān)Ocelot 使用Polly 處理部分失敗問題
原文地址:http://www.cnblogs.com/CreateMyself/p/7589397.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的弹性和瞬态故障处理库Polly介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么你需要将代码迁移到ASP.NET
- 下一篇: SQL Server 2017 正式发布