日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 默认站点!

默认站点

當(dāng)前位置: 首頁 >

【微服务学习】Polly:熔断降级组件

發(fā)布時(shí)間:2023/12/4 34 豆豆
默认站点 收集整理的這篇文章主要介紹了 【微服务学习】Polly:熔断降级组件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

何為熔斷降級

  “熔斷器如同電力過載保護(hù)器。它可以實(shí)現(xiàn)快速失敗,如果它在一段時(shí)間內(nèi)偵測到許多類似的錯(cuò)誤,會強(qiáng)迫其以后的多個(gè)調(diào)用快速失敗,不再訪問遠(yuǎn)程服務(wù)器,從而防止應(yīng)用程序不斷地嘗試執(zhí)行可能會失敗的操作,使得應(yīng)用程序繼續(xù)執(zhí)行而不用等待修正錯(cuò)誤,或者浪費(fèi)時(shí)間去等到長時(shí)間的超時(shí)產(chǎn)生。”   降級的目的是當(dāng)某個(gè)服務(wù)提供者發(fā)生故障的時(shí)候,向調(diào)用方返回一個(gè)替代響應(yīng)。

  簡單一句話概括,降級就是在調(diào)用的下游服務(wù)A出現(xiàn)問題(常見超時(shí)),提供PLAN-B,返回的效果可能沒有服務(wù)A好,但是聊勝于無。而熔斷器的存在就是要保障何時(shí)走到降級方法,何時(shí)恢復(fù),以什么樣的策略恢復(fù)。

.NET Core 熔斷降級實(shí)踐

簡介

  Polly是一種.NET彈性和瞬態(tài)故障處理庫,允許我們以非常順暢和線程安全的方式來執(zhí)諸如行重試,斷路,超時(shí),故障恢復(fù)等策略。  

Polly當(dāng)前版本可以工作在 .NET Standard 1.1 (包括: .NET Framework 4.5-4.6.1, .NET Core 1.0, Mono, Xamarin, UWP, WP8.1+) 和 .NET Standard 2.0+ (包括: .NET Framework 4.6.1, .NET Core 2.0+, 新版本的 Mono, Xamarin and UWP targets).上,同時(shí)也為舊版本的.NET Framework提供了一些可用的舊版本,具體版本對應(yīng)如下:

  該項(xiàng)目作者現(xiàn)已成為.NET基金會一員,項(xiàng)目一直在不停迭代和更新,項(xiàng)目地址【https://github.com/App-vNext/Polly】。

七種恢復(fù)策略

策略前置條件例此策略解決什么問題?
重試策略(Retry)?
(policy family)
(快速開始?;?深入學(xué)習(xí))
重試策略針對的前置條件是短暫的故障延遲且在短暫的延遲之后能夠自我糾正。"也許這只是曇花一現(xiàn)"允許我們做的是能夠自動配置重試機(jī)制。
斷路器(Circuit-breaker)
(policy family)
(快速開始?;?深入學(xué)習(xí))
斷路器策略針對的前置條件是當(dāng)系統(tǒng)繁忙時(shí),快速響應(yīng)失敗總比讓用戶一直等待更好。

保護(hù)系統(tǒng)故障免受過載,Polly可以幫其恢復(fù)。

"痛了,自然就會放下"

"讓它歇一下"

當(dāng)故障超過某個(gè)預(yù)先配置的閾值時(shí), 中斷電路 (塊執(zhí)行) 一段時(shí)間。
超時(shí)(Timeout)
(快速開始?;?深入學(xué)習(xí))
超時(shí)策略針對的前置條件是超過一定的等待時(shí)間,想要得到成功的結(jié)果是不可能的。"你不必等待,她不會再來"保證調(diào)用者不必等待太長時(shí)間。
隔板隔離(Bulkhead Isolation)
(快速開始?;?深入學(xué)習(xí))
隔板隔離針對的前置條件是當(dāng)進(jìn)程出現(xiàn)故障時(shí),多個(gè)失敗一直在主機(jī)中對資源(例如線程/ CPU)一直占用。下游系統(tǒng)故障也可能導(dǎo)致上游失敗。

這兩個(gè)風(fēng)險(xiǎn)都將造成嚴(yán)重的后果。

"一顆老鼠屎壞了一鍋湯"將受管制的操作限制在固定的資源池中,避免其他資源受其影響。
緩存(Cache)
(快速開始?;?深入學(xué)習(xí))
數(shù)據(jù)不會很頻繁的進(jìn)行更新,相同請求的響應(yīng)是相似的。"聽說?
你還會再來
我翹首以盼"
首次加載數(shù)據(jù)時(shí)將響應(yīng)數(shù)據(jù)進(jìn)行緩存,請求時(shí)若緩存中存在則直接從緩存中讀取。
回退(Fallback)
(快速開始?;?深入學(xué)習(xí))
操作將仍然失敗 - 但是你可以實(shí)現(xiàn)準(zhǔn)備好失敗后要做的補(bǔ)救措施。"你若安好,我備胎到老。"定義失敗時(shí)要返回 (或要執(zhí)行的操作) 的替代值。.
策略包裝(PolicyWrap)
(快速開始?;?深入學(xué)習(xí))
不同的故障需要不同的策略,也就意味著彈性靈活使用組合。"謀定而后動"允許靈活地組合上述任何策略。

實(shí)踐

故障處理(被動策略)

故障處理策略處理通過策略執(zhí)行的代碼所引發(fā)的特定的異常或返回結(jié)果。

第一步:指定希望處理的異常(可選-指定要處理的返回結(jié)果)

指定希望處理的異常:
// 單一異常種類
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")

// 普通異常或聚合異常的內(nèi)部異常, 可以帶有條件
Policy
.HandleInner<HttpRequestException>()
.OrInner<OperationCanceledException>(ex => ex.CancellationToken != myToken)
指定要處理的返回結(jié)果

從Polly v4.3.0起,包含返回TResult的調(diào)用的策略也可以處理TResult返回值

// 帶條件判斷的單種返回值處理
Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)

// 帶條件判斷的多種返回值處理
Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
.OrResult(r => r.StatusCode == HttpStatusCode.BadGateway)

// 原始返回值處理 (隱式調(diào)用 .Equals())
Policy
.HandleResult<HttpStatusCode>(HttpStatusCode.InternalServerError)
.OrResult(HttpStatusCode.BadGateway)

// 在一個(gè)策略中同時(shí)處理異常和返回值
HttpStatusCode[] httpStatusCodesWorthRetrying = {
HttpStatusCode.RequestTimeout, // 408
HttpStatusCode.InternalServerError, // 500
HttpStatusCode.BadGateway, // 502
HttpStatusCode.ServiceUnavailable, // 503
HttpStatusCode.GatewayTimeout // 504
};
HttpResponseMessage result = await Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
.RetryAsync(...)
.ExecuteAsync( /* Func<Task<HttpResponseMessage>> */ )

第二步:指定策略應(yīng)如何處理這些錯(cuò)誤

重試(Retry)
// 重試一次
Policy
.Handle<SomeExceptionType>()
.Retry()

// 重試多次
Policy
.Handle<SomeExceptionType>()
.Retry(3)

// 重試多次,每次重試觸發(fā)事件(參數(shù)為此次異常和當(dāng)前重試次數(shù))
Policy
.Handle<SomeExceptionType>()
.Retry(3, onRetry: (exception, retryCount) =>
{
// do something
});

// 重試多次,每次重試觸發(fā)事件(參數(shù)為此次異常、當(dāng)前重試次數(shù)和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.Retry(3, onRetry: (exception, retryCount, context) =>
{
// do something
});
不斷重試直到成功(Retry forever until succeeds)
// 不斷重試
Policy
.Handle<SomeExceptionType>()
.RetryForever()

// 不斷重試,每次重試觸發(fā)事件(參數(shù)為此次異常)
Policy
.Handle<SomeExceptionType>()
.RetryForever(onRetry: exception =>
{
// do something
});

// 不斷重試,每次重試觸發(fā)事件(參數(shù)為此次異常和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.RetryForever(onRetry: (exception, context) =>
{
// do something
});
等待并重試(Wait and retry)

WaitAndRetry策略處理HTTP狀態(tài)代碼429的重試后狀態(tài)

// 重試多次, 每次重試之間等待指定的持續(xù)時(shí)間。(失敗之后觸發(fā)等待, 然后再進(jìn)行下一次嘗試。)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
});

// 重試并觸發(fā)事件多次, 每次重試之間等待指定的持續(xù)時(shí)間。(事件參數(shù)為當(dāng)前異常和時(shí)間間隔)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan) => {
// do something
});

// 重試并觸發(fā)事件多次, 每次重試之間等待指定的持續(xù)時(shí)間。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan, context) => {
// do something
});

// 重試并觸發(fā)事件多次, 每次重試之間等待指定的持續(xù)時(shí)間。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔、當(dāng)前重試次數(shù)和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan, retryCount, context) => {
// do something
});

// 重試指定的次數(shù), 根據(jù)當(dāng)前重試次數(shù)計(jì)算等待時(shí)間 (允許指數(shù)回退)
// 當(dāng)前這種情況下, 等待時(shí)間為:
// 2 ^ 1 = 2 s
// 2 ^ 2 = 4 s
// 2 ^ 3 = 8 s
// 2 ^ 4 = 16 s
// 2 ^ 5 = 32 s
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(5, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);

// 重試指定的次數(shù),每次重試時(shí)觸發(fā)事件,根據(jù)當(dāng)前重試次數(shù)計(jì)算等待時(shí)間。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, context) => {
// do something
}
);

// 重試指定的次數(shù),每次重試時(shí)觸發(fā)事件,根據(jù)當(dāng)前重試次數(shù)計(jì)算等待時(shí)間。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔、當(dāng)前重試次數(shù)和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, retryCount, context) => {
// do something
}
);
不斷等待并重試直到成功(Wait and retry forever until succeeds)

如果所有重試都失敗, 重試策略將重新引發(fā)最后一個(gè)異常返回到調(diào)用代碼。

// 不斷等待并重試
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);

// 不斷等待并重試,每次重試時(shí)觸發(fā)事件。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timespan) =>
{
// do something
});


// 不斷等待并重試,每次重試時(shí)觸發(fā)事件。(事件參數(shù)為當(dāng)前異常、時(shí)間間隔和當(dāng)前執(zhí)行的上下文)
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timespan, context) =>
{
// do something
});
斷路器(Circuit-breaker)

斷路器策略通過在程序出錯(cuò)時(shí)拋出BrokenCircuitException來屏蔽其他異常。文檔 請注意, 斷路器策略將重新引發(fā)所有異常, 甚至是已處理的異常。所以使用時(shí)通常會將重試策略和斷路器策略組合使用

// 在指定數(shù)量的連續(xù)異常后斷開程序執(zhí)行并在之后的一段時(shí)間內(nèi)保持程序執(zhí)行斷開。
Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));

// 在指定數(shù)量的連續(xù)異常后斷開程序執(zhí)行并在之后的一段時(shí)間內(nèi)保持程序執(zhí)行斷開。當(dāng)程序執(zhí)行斷開或者重新啟用時(shí)觸發(fā)事件。(程序執(zhí)行斷開事件參數(shù)為當(dāng)前異常和間隔時(shí)間,重新啟用事件無參數(shù))
Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
Action onReset = () => { ... };
CircuitBreakerPolicy breaker = Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

// 在指定數(shù)量的連續(xù)異常后斷開程序執(zhí)行并在之后的一段時(shí)間內(nèi)保持程序執(zhí)行斷開。當(dāng)程序執(zhí)行斷開或者重新啟用時(shí)觸發(fā)事件。(程序執(zhí)行斷開事件參數(shù)為當(dāng)前異常、間隔時(shí)間和當(dāng)前執(zhí)行上下文,重新啟用事件參數(shù)為當(dāng)前執(zhí)行上下文)
Action<Exception, TimeSpan, Context> onBreak = (exception, timespan, context) => { ... };
Action<Context> onReset = context => { ... };
CircuitBreakerPolicy breaker = Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

// 程序運(yùn)行狀態(tài), 運(yùn)行狀況。
CircuitState state = breaker.CircuitState;

/*
CircuitState.Closed - 斷路器未觸發(fā),允許操作執(zhí)行。
CircuitState.Open - 斷路器開啟,阻止操作執(zhí)行。
CircuitState.HalfOpen - 斷路器開啟指定時(shí)間后重新關(guān)閉,此狀態(tài)允許操作執(zhí)行,之后的開啟或關(guān)閉取決于繼續(xù)執(zhí)行的結(jié)果。
CircuitState.Isolated - 斷路器被主動開啟,阻止操作執(zhí)行。
*/

// 手動打開 (并保持打開) 斷路器(例如需要主動隔離下游服務(wù)時(shí))
breaker.Isolate();
// 重置斷路器為關(guān)閉狀態(tài), 再次開始允許操作執(zhí)行。
breaker.Reset();
高級斷路器(Advanced Circuit Breaker)
// 在采樣持續(xù)時(shí)間內(nèi), 如果已處理異常的操作的比例超過故障閾值且該時(shí)間段內(nèi)通過請求操作數(shù)達(dá)到最小吞吐量,主動啟動斷路器。

Policy
.Handle<SomeExceptionType>()
.AdvancedCircuitBreaker(
failureThreshold: 0.5, // 當(dāng)>=50%的操作會導(dǎo)致已處理的異常時(shí)中斷程序。
samplingDuration: TimeSpan.FromSeconds(10), // 采樣時(shí)間區(qū)間為10秒
minimumThroughput: 8, // ... 在采樣時(shí)間區(qū)間內(nèi)進(jìn)行了至少8次操作。
durationOfBreak: TimeSpan.FromSeconds(30) // 斷路30秒.
);

// 采用狀態(tài)更改委托的配置重載同樣可用于高級斷路器。

// 電路狀態(tài)監(jiān)控和手動控制同樣也可用于高級斷路器。

更多相關(guān)資料請參考:?文檔

有關(guān)斷路器模式的更多信息, 請參見:

  • 改造 Netflix API 增加接口彈性

  • 斷路器淺談 (馬丁·福勒)

  • 斷路器模式 (Microsoft)

  • 原始斷路器鏈

回退策略(Fallback)
// 執(zhí)行錯(cuò)誤時(shí)提供替代值。
Policy
.Handle<Whatever>()
.Fallback<UserAvatar>(UserAvatar.Blank)

// 執(zhí)行錯(cuò)誤時(shí)使用回調(diào)函數(shù)提供替代值。
Policy
.Handle<Whatever>()
.Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }

// 執(zhí)行錯(cuò)誤時(shí)提供替代值的同時(shí)觸發(fā)事件。(事件參數(shù)為當(dāng)前異常信息和當(dāng)前運(yùn)行上下文)
Policy
.Handle<Whatever>()
.Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) =>
{
// do something
});

第三步:執(zhí)行策略

// 執(zhí)行操作
var policy = Policy
.Handle<SomeExceptionType>()
.Retry();

policy.Execute(() => DoSomething());

// 執(zhí)行傳遞任意上下文數(shù)據(jù)的操作
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" }}
);

// 執(zhí)行返回結(jié)果的函數(shù)
var policy = Policy
.Handle<SomeExceptionType>()
.Retry();

var result = policy.Execute(() => DoSomething());

// 執(zhí)行傳遞任意上下文數(shù)據(jù)且返回結(jié)果的操作
var policy = Policy
.Handle<SomeExceptionType>()
.Retry(3, (exception, retryCount, context) =>
{
object methodThatRaisedException = context["methodName"];
Log(exception, methodThatRaisedException)
});

var result = policy.Execute(
() => DoSomething(),
new Dictionary<string, object>() {{ "methodName", "some method" }}
);

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

為了簡單起見, 上面的示例顯示了策略定義, 然后是策略執(zhí)行。但是在代碼庫和應(yīng)用程序生命周期中, 策略定義和執(zhí)行可能同樣經(jīng)常被分離。例如, 可以選擇在啟動時(shí)定義策略, 然后通過依賴注入將其提供給使用點(diǎn)。

故障處理(主動策略)

主動策略添加了不基于當(dāng)策略被引發(fā)或返回時(shí)才處理錯(cuò)誤的彈性策略。

第一步:配置

超時(shí)(Timeout)
樂觀超時(shí)(Optimistic timeout)

樂觀超時(shí)通過 CancellationToken 運(yùn)行, 并假定您執(zhí)行支持合作取消的委托。您必須使用 Execute/Async(...)?重載以獲取?CancellationToken, 并且執(zhí)行的委托必須遵守該?CancellationToken。

// 如果執(zhí)行的委托尚未完成,在調(diào)用30秒后超時(shí)并返回 。樂觀超時(shí): 委托應(yīng)采取并遵守 CancellationToken。
Policy
.Timeout(30)

// 使用 TimeSpan 配置超時(shí)。
Policy
.Timeout(TimeSpan.FromMilliseconds(2500))

// 通過方法提供可變的超時(shí)。
Policy
.Timeout(() => myTimeoutProvider)) // Func<TimeSpan> myTimeoutProvider

// 超時(shí)后觸發(fā)事件。(事件參數(shù)為當(dāng)前執(zhí)行上下文、執(zhí)行間隔、當(dāng)前執(zhí)行的TASK)
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
// do something
});

// 示例:在超時(shí)后記錄日志
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
logger.Warn($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds.");
});

// 示例:在超時(shí)任務(wù)完成時(shí)捕獲該任務(wù)中的任何異常
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
task.ContinueWith(t => {
if (t.IsFaulted) logger.Error($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds, with: {t.Exception}.");
});
});

示例執(zhí)行:

Policy timeoutPolicy = Policy.TimeoutAsync(30);
HttpResponseMessage httpResponse = await timeoutPolicy
.ExecuteAsync(
async ct => await httpClient.GetAsync(endpoint, ct), // 執(zhí)行一個(gè)有參數(shù)且響應(yīng) CancellationToken 的委托。
CancellationToken.None // 在這種情況下, CancellationToken.None 將被傳遞到執(zhí)行中, 這表明您沒有將期望的令牌控制通過超時(shí)策略添加。自定義 CancellationToken 也可以通過,詳情請參閱 wiki 中的例子。
);
悲觀超時(shí)(Pessimistic timeout)

悲觀超時(shí)允許調(diào)用代碼 "離開" 等待執(zhí)行完成的委托, 即使它不支持取消。在同步執(zhí)行中, 這是以犧牲一個(gè)額外的線程為代價(jià)的。有關(guān)更多細(xì)節(jié), 請參見文檔。示例執(zhí)行:

Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Pessimistic);
var response = await timeoutPolicy
.ExecuteAsync(
async () => await FooNotHonoringCancellationAsync(), // 執(zhí)行不接受取消令牌且不響應(yīng)取消的委托。
);

超時(shí)策略在發(fā)生超時(shí)時(shí)引發(fā)?TimeoutRejectedException。更多詳情參見文檔。

隔板(Bulkhead)
// 通過該策略將執(zhí)行限制為最多12個(gè)并發(fā)操作。
Policy
.Bulkhead(12)

// 將通過策略執(zhí)行的操作限制為最多12個(gè)并發(fā)操作, 如果插槽都被占滿, 最多可以有兩個(gè)操作被等待執(zhí)行。
Policy
.Bulkhead(12, 2)

// 限制并發(fā)執(zhí)行, 如果執(zhí)行被拒絕, 則調(diào)用觸發(fā)事件。(事件參數(shù)為當(dāng)前執(zhí)行上下文)
Policy
.Bulkhead(12, context =>
{
// do something
});

// 查看隔板可用容量, 例如健康負(fù)荷。
var bulkhead = Policy.Bulkhead(12, 2);
// ...
int freeExecutionSlots = bulkhead.BulkheadAvailableCount;
int freeQueueSlots = bulkhead.QueueAvailableCount;

當(dāng)隔板策略的插槽全部被正在執(zhí)行的操作占滿是,會引發(fā)?BulkheadRejectedException。更多詳情參見文檔。

緩存(Cache)
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var memoryCacheProvider = new MemoryCacheProvider(memoryCache);
var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5));

// .NET Core CacheProviders DI 示例 請參照以下文章 https://github.com/App-vNext/Polly/wiki/Cache#working-with-cacheproviders :
// - https://github.com/App-vNext/Polly.Caching.MemoryCache
// - https://github.com/App-vNext/Polly.Caching.IDistributedCache

// 定義每天午夜絕對過期的緩存策略。
var cachePolicy = Policy.Cache(memoryCacheProvider, new AbsoluteTtl(DateTimeOffset.Now.Date.AddDays(1));

// 定義超時(shí)過期的緩存策略: 每次使用緩存項(xiàng)時(shí), 項(xiàng)目的有效期為5分鐘。
var cachePolicy = Policy.Cache(memoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(5));

// 定義緩存策略, 并捕獲任何緩存提供程序錯(cuò)誤以進(jìn)行日志記錄。
var cachePolicy = Policy.Cache(myCacheProvider, TimeSpan.FromMinutes(5),
(context, key, ex) => {
logger.Error($"Cache provider, for key {key}, threw exception: {ex}."); // (for example)
}
);

// 以直通緩存的身份執(zhí)行緩存: 首先檢查緩存;如果未找到, 請執(zhí)行基礎(chǔ)委托并將結(jié)果存儲在緩存中。
// 用于特定執(zhí)行的緩存的鍵是通過在傳遞給執(zhí)行的上下文實(shí)例上設(shè)置操作鍵 (v6 之前: 執(zhí)行鍵) 來指定的。使用下面顯示的窗體的重載 (或包含相同元素的更豐富的重載)。
// 示例: "fookey" 是將在下面的執(zhí)行中使用的緩存密鑰。
TResult result = cachePolicy.Execute(context => getFoo(), new Context("FooKey"));

有關(guān)使用其他緩存提供程序的更豐富的選項(xiàng)和詳細(xì)信息, 請參閱:文檔

策略包裝(PolicyWrap)
// 定義由以前定義的策略構(gòu)建的組合策略。
var policyWrap = Policy
.Wrap(fallback, cache, retry, breaker, timeout, bulkhead);
// (包裝策略執(zhí)行任何被包裝的策略: fallback outermost ... bulkhead innermost)
policyWrap.Execute(...)

// 定義標(biāo)準(zhǔn)的彈性策略
PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout);

// ... 然后包裝在額外的策略特定于一個(gè)請求類型:
Avatar avatar = Policy
.Handle<Whatever>()
.Fallback<Avatar>(Avatar.Blank)
.Wrap(commonResilience)
.Execute(() => { /* get avatar */ });

// 共享通用彈性, 但將不同的策略包裝在另一個(gè)請求類型中:
Reputation reps = Policy
.Handle<Whatever>()
.Fallback<Reputation>(Reputation.NotAvailable)
.Wrap(commonResilience)
.Execute(() => { /* get reputation */ });

更多詳情參見文檔

無策略(NoOp)
// 定義一個(gè)策略, 該策略將簡單地導(dǎo)致傳遞給執(zhí)行的委托 "按原樣" 執(zhí)行。
// 適用于在單元測試中或在應(yīng)用程序中可能需要策略, 但您只是希望在沒有策略干預(yù)的情況下通過執(zhí)行的應(yīng)用程序。
NoOpPolicy noOp = Policy.NoOp();

更多詳情參見文檔

第二步:執(zhí)行策略

同上

執(zhí)行后:捕獲結(jié)果或任何最終異常

使用 ExecuteAndCapture(...) 方法可以捕獲執(zhí)行的結(jié)果: 這些方法返回一個(gè)執(zhí)行結(jié)果實(shí)例, 該實(shí)例描述的是成功執(zhí)行還是錯(cuò)誤。

var policyResult = await Policy
.Handle<HttpRequestException>()
.RetryAsync()
.ExecuteAndCaptureAsync(() => DoSomethingAsync());
/*
policyResult.Outcome - 調(diào)用是成功還是失敗
policyResult.FinalException - 最后一個(gè)異常。如果調(diào)用成功, 則捕獲的最后一個(gè)異常將為 null
policyResult.ExceptionType - 定義為要處理的策略的最后一個(gè)異常 (如上面的 HttpRequestException) 或未處理的異常 (如 Exception). 如果調(diào)用成功, 則為 null。
policyResult.Result - 如果執(zhí)行 func, 調(diào)用成功則返回執(zhí)行結(jié)果, 否則為類型的默認(rèn)值
*/

處理返回值和?Policy<TResult>

如步驟1b 所述, 從 polly v4.3.0 開始, 策略可以組合處理返回值和異常:

// 在一個(gè)策略中處理異常和返回值
HttpStatusCode[] httpStatusCodesWorthRetrying = {
HttpStatusCode.RequestTimeout, // 408
HttpStatusCode.InternalServerError, // 500
HttpStatusCode.BadGateway, // 502
HttpStatusCode.ServiceUnavailable, // 503
HttpStatusCode.GatewayTimeout // 504
};
HttpResponseMessage result = await Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
.RetryAsync(...)
.ExecuteAsync( /* some Func<Task<HttpResponseMessage>> */ )

要處理的異常和返回結(jié)果可以以任意順序流暢的表達(dá)。

強(qiáng)類型?Policy<TResult>

配置策略 .HandleResult<TResult>(...) 或.OrResult<TResult>(...) 生成特定強(qiáng)類型策略 Policy<TResult>,例如 Retry<TResult>, AdvancedCircuitBreaker<TResult>。這些策略必須用于執(zhí)行返回 TResult 的委托, 即:

  • Execute(Func<TResult>) (and related overloads)

  • ExecuteAsync(Func<CancellationToken, Task<TResult>>) (and related overloads)

ExecuteAndCapture<TResult>()

.ExecuteAndCapture(...) 在非泛型策略上返回具有屬性的 PolicyResult:

policyResult.Outcome - 調(diào)用是成功還是失敗
policyResult.FinalException - 最后一個(gè)異常。如果調(diào)用成功, 則捕獲的最后一個(gè)異常將為 null
policyResult.ExceptionType - 定義為要處理的策略的最后一個(gè)異常 (如上面的 HttpRequestException) 或未處理的異常 (如 Exception). 如果調(diào)用成功, 則為 null。
policyResult.Result - 如果執(zhí)行 func, 調(diào)用成功則返回執(zhí)行結(jié)果, 否則為類型的默認(rèn)值

.ExecuteAndCapture<TResult>(Func<TResult>)在強(qiáng)類型策略上添加了兩個(gè)屬性:

policyResult.FaultType - 最終的故障是處理異常還是由策略處理的結(jié)果?如果委托執(zhí)行成功, 則為 null。
policyResult.FinalHandledResult - 處理的最終故障結(jié)果;如果調(diào)用成功將為空或類型的默認(rèn)值。

Policy<TResult>策略的狀態(tài)更改事件

在僅處理異常的非泛型策略中, 狀態(tài)更改事件 (如 onRetry 和 onBreak ) 提供 Exception 參數(shù)。在處理 TResult 返回值的通用性策略中, 狀態(tài)更改委托是相同的, 除非它們采用 DelegateResult參數(shù)代替異常。DelegateResult具有兩個(gè)屬性:

  • Exception?// 如果策略正在處理異常則為則剛剛引發(fā)異常(否則為空),

  • Result?// 如果策略正在處理結(jié)果則為剛剛引發(fā)的 TResult (否則為 default(TResult))

BrokenCircuitException<TResult>

非通用的循環(huán)斷路器策略在斷路時(shí)拋出一個(gè)BrokenCircuitException。此 BrokenCircuitException 包含最后一個(gè)異常 (導(dǎo)致中斷的異常) 作為 InnerException。關(guān)于 CircuitBreakerPolicy<TResult>?策略:

  • 由于異常而中斷將引發(fā)一個(gè) BrokenCircuitException, 并將 InnerException 設(shè)置為觸發(fā)中斷的異常 (如以前一樣)。

  • 由于處理結(jié)果而中斷會引發(fā) 'BrokenCircuitException<TResult>', 其 Result 屬性設(shè)置為導(dǎo)致電路中斷的結(jié)果.

Policy Keys 與 Context data

// 用擴(kuò)展方法 WithPolicyKey() 使用 PolicyKey 識別策略,
// (例如, 對于日志或指標(biāo)中的相關(guān)性)

var policy = Policy
.Handle<DataAccessException>()
.Retry(3, onRetry: (exception, retryCount, context) =>
{
logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.ExecutionKey}, due to: {exception}.");
})
.WithPolicyKey("MyDataAccessPolicy");

// 在上下文中傳遞 ExecutionKey , 并使用 ExecutionKey 標(biāo)識呼叫站點(diǎn)
var customerDetails = policy.Execute(myDelegate, new Context("GetCustomerDetails"));

// "MyDataAccessPolicy" -> context.PolicyKey
// "GetCustomerDetails -> context.ExecutionKey


// 將其他自定義信息從調(diào)用站點(diǎn)傳遞到執(zhí)行上下文中
var policy = Policy
.Handle<DataAccessException>()
.Retry(3, onRetry: (exception, retryCount, context) =>
{
logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.ExecutionKey}, getting {context["Type"]} of id {context["Id"]}, due to: {exception}.");
})
.WithPolicyKey("MyDataAccessPolicy");

int id = ... // 客戶id
var customerDetails = policy.Execute(context => GetCustomer(id),
new Context("GetCustomerDetails", new Dictionary<string, object>() {{"Type","Customer"},{"Id",id}}

更多資料參考文檔

PolicyRegistry

// 創(chuàng)建策略注冊表 (例如在應(yīng)用程序啟動時(shí))
PolicyRegistry registry = new PolicyRegistry();

// 使用策略填充注冊表
registry.Add("StandardHttpResilience", myStandardHttpResiliencePolicy);
// 或者:
registry["StandardHttpResilience"] = myStandardHttpResiliencePolicy;

// 通過 DI 將注冊表實(shí)例傳遞給使用站點(diǎn)
public class MyServiceGateway
{
public void MyServiceGateway(..., IReadOnlyPolicyRegistry<string> registry, ...)
{
...
}
}
// (或者, 如果您更喜歡環(huán)境上下文模式, 請使用線程安全的單例)

// 使用注冊表中的策略
registry.Get<IAsyncPolicy<HttpResponseMessage>>("StandardHttpResilience")
.ExecuteAsync<HttpResponseMessage>(...)

策略注冊表具有一系列進(jìn)一步的類似字典的語義, 例如 .ContainsKey(...), .TryGet(...), .Count, .Clear(), 和 Remove(...),適用于 v5.2.0 以上版本

有關(guān)詳細(xì)信息, 請參閱:?文檔

.NET Core 使用Polly重試機(jī)制

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));
}
public async Task<IHttpActionResult> Get()
{
var httpClient = new HttpClient();
var requestEndpoint = "http://www.baidu.com";

HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));

IEnumerable<string> numbers = await httpResponse.Content.ReadAsAsync<IEnumerable<string>>();

return Ok(numbers);
}
}

原文地址:https://www.cnblogs.com/WayneShao/p/10672101.html

.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

總結(jié)

以上是默认站点為你收集整理的【微服务学习】Polly:熔断降级组件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得默认站点網(wǎng)站內(nèi)容還不錯(cuò),歡迎將默认站点推薦給好友。