Skip to main content

Polly 韌性策略指南

用途:為 .NET 應用程式提供韌性(Resilience)和暫態故障處理能力

NuGetPolly(v8+)/ Microsoft.Extensions.Resilience(DI 整合)

授權:BSD-3-Clause

支援:.NET 6+、.NET Framework 4.7.2+


什麼是韌性策略

韌性(Resilience) 是指系統在遇到暫態故障時,能夠自動恢復正常運作的能力。暫態故障(Transient Fault) 是指會自行消失的短暫錯誤,例如:

  • 網路連線瞬斷
  • PLC 忙碌暫時無法回應
  • 設備重啟期間的通訊中斷
  • MES/ERP API 回應逾時

這些錯誤的共同特點是:過一下子再試就好了。手寫 try-catch + Thread.Sleep 重試當然可以,但一旦邏輯變複雜(指數退避、熔斷、逾時、fallback 組合),程式碼就會變得難以維護。

Polly 將這些韌性策略抽象為可組合的元件,讓你用宣告式方式定義「遇到錯誤怎麼辦」。


Polly v8 架構:ResiliencePipeline

warning

Polly v8(2023 年發布)架構大幅改版。如果你看到 Policy.Handle<T>()PolicyBuilderISyncPolicy 等 API,那是 v7 的舊 API。v8 改用 ResiliencePipelineResiliencePipelineBuilder

v7 vs v8 核心差異

項目v7(舊)v8(新)
核心抽象Policy / AsyncPolicyResiliencePipeline
建構方式Policy.Handle<T>().RetryAsync()new ResiliencePipelineBuilder().AddRetry()
同步/非同步分開的 API統一 API
泛型Policy<T> / AsyncPolicy<T>ResiliencePipeline<T>
DI 整合需額外包裝原生 AddResiliencePipeline
策略名稱PolicyStrategy
組合方式Policy.WrapResiliencePipelineBuilder 鏈式呼叫

ResiliencePipeline 概念

ResiliencePipeline 是一個由多個策略組成的管道。請求依序通過每個策略,每個策略都可以決定如何處理錯誤。下圖示意一個同時包含 Retry、CircuitBreaker、Timeout、Fallback 的典型管道:

各策略的職責:

  • Retry:在暫態錯誤時自動重試,常搭配指數退避
  • CircuitBreaker:失敗率超過閾值時熔斷,避免打爆已故障的下游
  • Timeout:限制單次呼叫的最長等待時間,避免執行緒被長時間佔用
  • Fallback:當前面所有策略都無法恢復時,回傳備援值或執行替代邏輯

加入順序決定策略的巢狀關係:在 ResiliencePipelineBuilder 中,先加入的為外層,後加入的為內層。

Circuit Breaker 狀態機

CircuitBreaker 本身就是一個三態狀態機,理解它的狀態轉換是使用這個策略的前提:

三個狀態的行為:

狀態行為何時轉出
Closed正常放行所有請求失敗率達閾值 → Open
Open直接拒絕請求(fail-fast),不打下游冷卻時間到期 → HalfOpen
HalfOpen放行少量試探請求探測下游恢復狀況成功 → Closed;失敗 → Open

關鍵設定(詳見核心策略詳解):

  • FailureRatio:失敗率閾值(0.0 ~ 1.0)
  • MinimumThroughput:取樣視窗內最低請求數,避免樣本太少誤判
  • SamplingDuration:統計失敗率的時間視窗
  • BreakDuration:進入 Open 後的冷卻時間

Polly vs 手寫 try-catch-retry

// ❌ 手寫:邏輯散亂、難以維護
async Task<int> ReadPlcWithRetry()
{
int retries = 0;
while (true)
{
try
{
return await plcClient.ReadRegisterAsync("D100");
}
catch (CommunicationException) when (retries < 3)
{
retries++;
var delay = TimeSpan.FromSeconds(Math.Pow(2, retries));
Logger.Warn($"讀取失敗,{delay.TotalSeconds}s 後重試 ({retries}/3)");
await Task.Delay(delay);
}
}
}

// ✅ Polly:宣告式、可組合、可測試
var pipeline = new ResiliencePipelineBuilder<int>()
.AddRetry(new RetryStrategyOptions<int>
{
MaxRetryAttempts = 3,
BackoffType = DelayBackoffType.Exponential,
Delay = TimeSpan.FromSeconds(1),
OnRetry = args =>
{
Logger.Warn($"讀取失敗,重試 {args.AttemptNumber}/3");
return ValueTask.CompletedTask;
}
})
.Build();

var value = await pipeline.ExecuteAsync(
async ct => await plcClient.ReadRegisterAsync("D100", ct));

Polly 的優勢:

  • 宣告式:策略定義與業務邏輯分離
  • 可組合:Retry + Timeout + CircuitBreaker 可自由組合
  • 可測試:策略可注入替換
  • 可觀測:內建事件回呼(OnRetry、OnBreak 等)
  • DI 友好:透過 AddResiliencePipeline 註冊

安裝

核心套件

dotnet add package Polly.Core

DI 整合(推薦)

dotnet add package Microsoft.Extensions.Resilience

HTTP 韌性(呼叫 MES/ERP API)

dotnet add package Microsoft.Extensions.Http.Resilience

常用 namespace

using Polly;
using Polly.Retry;
using Polly.CircuitBreaker;
using Polly.Timeout;
using Polly.Fallback;
using Polly.RateLimiting;
using Polly.Hedging;

最小範例

// 建立韌性管道
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(1),
BackoffType = DelayBackoffType.Exponential
})
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();

// 使用管道執行操作
await pipeline.ExecuteAsync(async ct =>
{
await plcClient.ReadRegisterAsync("D100", ct);
});

本指南結構

頁面內容
概觀(本頁)ResiliencePipeline 架構、安裝、入門
核心策略詳解Retry、CircuitBreaker、Timeout、Fallback、RateLimiter、Hedging
公司場景 PatternPLC 通訊管道、SECS/GEM 重試、HTTP 韌性、Rx.NET 搭配
最佳實踐Transient vs Permanent、閾值設定、測試策略