Polly logo

Polly

Community
znlgis
polly

Use when adding retry, timeout, circuit-breaker, rate-limit or fallback resilience to .NET HTTP/database calls — ResiliencePipelineBuilder AddRetry/AddTimeout/AddCircuitBreaker, Polly 8 ResiliencePipeline, Microsoft.Extensions.Http.Resilience AddStandardResilienceHandler, or fixing TimeoutRejectedException, retries multiplying load, and 'The circuit is now open'. Polly: .NET resilience and transient-fault-handling library.

Overview

Publisherznlgis
Repositoryopengis-skills
Skill namepolly
Stars
76
Forks
12
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by znlgis on GitHub. Read the source before you install it.

Installation

Install the Polly AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/znlgis/opengis-skills/tree/main/csharp/polly
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/znlgis/opengis-skills.git /tmp/opengis-skills
mkdir -p .claude/skills
cp -r /tmp/opengis-skills/csharp/polly .claude/skills/polly
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Polly in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Polly on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Polly is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

项目地址: https://github.com/App-vNext/Polly

官方文档: https://www.pollydocs.org/

NuGet: Polly.Core 8.8.1(v8 管道与策略)、Polly.Extensions 8.8.1(DI 注册 + 遥测)、Polly.RateLimiting 8.8.1(限流策略)、Polly 8.8.1(仅承载 v7 旧 API)

配套: Microsoft.Extensions.Http.Resilience 10.11.0(IHttpClientFactory 标准弹性 handler,版本跟随 Microsoft.Extensions.*)

目标框架: net8.0 / net6.0 / netstandard2.0 / .NET Framework 4.6.2+

许可证: BSD-3-Clause

概述

Polly 是 .NET 的弹性与瞬时故障处理库。v8 起以 ResiliencePipeline 为核心:把若干策略(retry / timeout / circuit breaker / rate limiter / hedging / fallback)组合成一条管道,业务代码只负责把委托交给管道执行。

  • 策略组合:AddRetry、AddTimeout、AddCircuitBreaker 可链式叠加,顺序即执行顺序(外层先执行)
  • 同步/异步统一:Execute / ExecuteAsync,异步重载接收 CancellationToken
  • 结果型重试:ShouldHandle = PredicateBuilder.HandleResult(...) 能对 HttpResponseMessage 等返回值判定,而不只是异常
  • DI 原生支持:AddResiliencePipeline(name, builder => ...) 注册命名管道,ResiliencePipelineProvider<string> 按名解析
  • 可观测:Polly.Extensions 提供 Telemetry(日志 + 指标),与 OpenTelemetry 打通

v7 的 Policy.Handle<...>().Retry(...) 写法仍可用但不推荐:它们只存在于 Polly 包中,v8 新能力(hedging、结果型管道、DI 管道、遥测)都不会覆盖旧 API。

包内容什么时候用
Polly.CoreResiliencePipelineBuilder、retry / timeout / circuit-breaker / fallback / hedging 策略新代码默认选它
Polly.ExtensionsAddResiliencePipeline(...)、ConfigureTelemetry(...)、DI 与遥测需要在 IServiceCollection 里配管道
Polly.RateLimitingAddRateLimiter(...) / RateLimiterStrategyOptions需要限流策略时单独安装(不在 Polly.Core 里)
Pollyv7 旧 API:Policy / AsyncPolicy / BrokenCircuitException只在维护既有 v7 代码时
Microsoft.Extensions.Http.ResilienceAddStandardResilienceHandler 等 HTTP 专用 handler用 IHttpClientFactory 调外部 HTTP

安装

方式一:dotnet CLI

bash
# 只用管道本体
dotnet add package Polly.Core

# 需要 DI 注册 / 遥测
dotnet add package Polly.Extensions

# 需要限流策略(独立包)
dotnet add package Polly.RateLimiting

# 维护 v7 旧代码
dotnet add package Polly

# HttpClient 标准弹性(含重试/熔断/超时/限流)
dotnet add package Microsoft.Extensions.Http.Resilience

方式二:PackageReference

xml
<ItemGroup>
  <PackageReference Include="Polly.Core" Version="8.8.1" />
  <PackageReference Include="Polly.Extensions" Version="8.8.1" />
  <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.11.0" />
</ItemGroup>

验证安装

bash
dotnet list package | grep -i polly

最小可运行验证:

csharp
using Polly;

var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 2 })
    .AddTimeout(TimeSpan.FromSeconds(5))
    .Build();

int attempt = 0;
var result = await pipeline.ExecuteAsync(async token =>
{
    attempt++;
    if (attempt < 3) throw new HttpRequestException("transient");
    return "ok";
});
Console.WriteLine($"result={result} attempts={attempt}");   // result=ok attempts=3

核心 API 速查

策略构建方法关键选项
重试AddRetry(new RetryStrategyOptions { ... })MaxRetryAttempts、Delay、BackoffType、UseJitter、ShouldHandle
超时AddTimeout(TimeSpan) / AddTimeout(new TimeoutStrategyOptions)TimeoutGenerator、OnTimeout
熔断AddCircuitBreaker(new CircuitBreakerStrategyOptions { ... })FailureRatio、MinimumThroughput、SamplingDuration、BreakDuration
限流AddRateLimiter(new RateLimiterStrategyOptions { ... })需 Polly.RateLimiting 包;DefaultRateLimiterOptions、OnRejected
对冲AddHedging(new HedgingStrategyOptions<T> { ... })MaxHedgedAttempts、Delay、ActionGenerator
降级AddFallback(new FallbackStrategyOptions<T> { ... })ShouldHandle、FallbackAction
管道执行Execute / ExecuteAsync委托接收 CancellationToken

重试:指数退避 + 抖动

csharp
using Polly;
using Polly.Retry;

var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        ShouldHandle = new PredicateBuilder()
            .Handle<HttpRequestException>()
            .Handle<TimeoutRejectedException>(),
        MaxRetryAttempts = 4,
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,                    // 避免重试风暴同步
        Delay = TimeSpan.FromSeconds(1),
        OnRetry = args =>
        {
            Console.WriteLine($"retry #{args.AttemptNumber} after {args.RetryDelay}");
            return default;
        }
    })
    .Build();

await pipeline.ExecuteAsync(async token => await httpClient.GetAsync("https://api.example.com/health", token), ct);

MaxRetryAttempts = 4 表示额外重试 4 次(总共最多 5 次调用),这是最常见的 off-by-one 误解。

超时:必须传 CancellationToken

csharp
var pipeline = new ResiliencePipelineBuilder()
    .AddTimeout(new TimeoutStrategyOptions
    {
        Timeout = TimeSpan.FromSeconds(3),
        OnTimeout = args =>
        {
            Console.WriteLine($"{args.Context.OperationKey} timed out after {args.Timeout}");
            return default;
        }
    })
    .Build();

// 委托必须接收并使用 token,否则超时无法中断底层调用
await pipeline.ExecuteAsync(async token => await db.QueryAsync(sql, token), ct);

超时策略靠协作式取消生效:委托里不把 token 传给下游,AddTimeout 到点只会抛出 TimeoutRejectedException,而不会真正终止正在跑的 IO。

熔断:失败率触发

csharp
using Polly.CircuitBreaker;

var pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 2 })
    .AddCircuitBreaker(new CircuitBreakerStrategyOptions
    {
        FailureRatio = 0.5,
        MinimumThroughput = 10,
        SamplingDuration = TimeSpan.FromSeconds(30),
        BreakDuration = TimeSpan.FromSeconds(15),
        ShouldHandle = new PredicateBuilder().Handle<HttpRequestException>(),
        OnOpened = args =>
        {
            Console.WriteLine($"circuit opened for {args.BreakDuration}");
            return default;
        }
    })
    .Build();

熔断器统计的是"最近 SamplingDuration 内至少 MinimumThroughput 次调用"的失败比例;熔断打开期间所有执行被快速拒绝,抛 ExecutionRejectedException 的派生异常,不再打下游。注意 BrokenCircuitException 只存在于 v7 的 Polly 包,Polly.Core(v8)里没有这个类型。

限流

csharp
// 需额外安装 Polly.RateLimiting,它不在 Polly.Core 中
using Polly.RateLimiting;

var pipeline = new ResiliencePipelineBuilder()
    .AddRateLimiter(new RateLimiterStrategyOptions
    {
        DefaultRateLimiterOptions = new ConcurrencyLimiterOptions
        {
            PermitLimit = 20,
            QueueLimit = 100,
            QueueProcessingOrder = QueueProcessingOrder.OldestFirst
        }
    })
    .Build();

在 DI 中注册命名管道

csharp
builder.Services.AddResiliencePipeline("orders-api", (pipelineBuilder, context) =>
{
    pipelineBuilder
        .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3, Delay = TimeSpan.FromMilliseconds(300) })
        .AddTimeout(TimeSpan.FromSeconds(10));
});

public class OrderClient(ResiliencePipelineProvider<string> pipelines)
{
    public Task<string> GetAsync(CancellationToken ct)
    {
        var pipeline = pipelines.GetPipeline("orders-api");
        return pipeline.ExecuteAsync(async token => await Fetch(token), ct).AsTask();
    }
}

HttpClient 标准弹性

csharp
builder.Services.AddHttpClient("inventory")
    .AddStandardResilienceHandler();   // 重试 + 熔断 + 总超时 + 尝试超时,内置合理默认值

// 需要覆盖默认值时
builder.Services.AddHttpClient("inventory")
    .AddStandardResilienceHandler(options =>
    {
        options.Retry.MaxRetryAttempts = 3;
        options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(5);
        options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(20);
    });

AddStandardResilienceHandler 来自 Microsoft.Extensions.Http.Resilience,与 Polly.Core 的管道是同一套引擎,只是把 HTTP 场景的默认参数和指标埋点打好了。


典型工作流

场景:给一个调用第三方支付网关的 ASP.NET Core API 加上弹性保护,要求"抖动不重试、超时快速失败、持续故障时熔断不再打下游,并能在日志里看见"。

输入

  • 现有 HttpClient 直连支付网关,偶发 HttpRequestException 与 504
  • 目标:瞬时故障自动恢复;下游长时间不可用时保护自己

步骤

1. 加包

bash
dotnet add package Polly.Core
dotnet add package Polly.Extensions

2. 定义管道(重试在内层,熔断在外层)

顺序很关键:AddRetry(...).AddCircuitBreaker(...) 表示熔断包住重试,即"重试耗尽仍失败 = 熔断器记一次失败";反过来会让每次重试都独立参与熔断统计。

csharp
builder.Services.AddResiliencePipeline("pay-gateway", pipelineBuilder =>
{
    pipelineBuilder
        .AddRetry(new RetryStrategyOptions
        {
            ShouldHandle = new PredicateBuilder()
                .Handle<HttpRequestException>()
                .HandleResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500),
            MaxRetryAttempts = 3,
            BackoffType = DelayBackoffType.Exponential,
            UseJitter = true,
            Delay = TimeSpan.FromMilliseconds(200)
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            MinimumThroughput = 8,
            SamplingDuration = TimeSpan.FromSeconds(30),
            BreakDuration = TimeSpan.FromSeconds(20)
        })
        .AddTimeout(TimeSpan.FromSeconds(15));
});

3. 业务侧经管道调用

csharp
public class PayGatewayClient(
    HttpClient http,
    ResiliencePipelineProvider<string> pipelines,
    ILogger<PayGatewayClient> logger)
{
    public async Task<string> ChargeAsync(string payload, CancellationToken ct)
    {
        var pipeline = pipelines.GetPipeline("pay-gateway");
        try
        {
            var response = await pipeline.ExecuteAsync(
                async token => await http.PostAsync("/charge", new StringContent(payload), token), ct);
            return await response.Content.ReadAsStringAsync(ct);
        }
        catch (ExecutionRejectedException ex)
        {
            // v8:熔断/限流/超时被拒绝时抛 ExecutionRejectedException 及其子类
            logger.LogWarning(ex, "pay gateway rejected by pipeline, falling back to queue");
            throw new InvalidOperationException("gateway unavailable, retry later", ex);
        }
    }
}

4. 打开遥测确认策略命中

csharp
builder.Services.AddResiliencePipeline("pay-gateway", (pipelineBuilder, context) =>
{
    pipelineBuilder.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 });
    pipelineBuilder.ConfigureTelemetry(context.ServiceProvider.GetRequiredService<ILoggerFactory>());
});

Telemetry 会输出 Resilience event occurred. EventName: 'OnRetry' 这类结构化日志,可直接被 Serilog / OpenTelemetry 采集。

5. 验证

bash
# 用一个会间歇 500 的假下游验证重试与熔断
dotnet run --project src/MyApi
for i in $(seq 1 30); do curl -s -o /dev/null -w "%{http_code} " http://localhost:5080/api/pay/charge; done; echo

输出

  • 偶发失败:日志出现 OnRetry 事件,请求最终成功
  • 持续故障:MinimumThroughput 内的失败率达到阈值后熔断打开,后续请求快速失败(ExecutionRejectedException),20 秒后自动半开试探
  • 全程每个请求有总耗时上限(15 秒),不会无限挂起

最佳实践

  1. 只对瞬态故障重试:4xx、参数错误、幂等性不确定的写操作不该重试;用 ShouldHandle 精确限定
  2. 重试必须退避 + 抖动:BackoffType.Exponential + UseJitter = true,否则多实例会同时重试放大下游压力
  3. 重试次数 ≤ 3~4:重试是延迟而非修复,次数越多对下游越不友好
  4. 超时必须把 token 传到底:委托签名带 CancellationToken 并一路透传给 HttpClient / EF Core / Dapper
  5. 熔断放外层、重试放内层:让熔断统计的是"一次完整业务调用"的成败
  6. 熔断打开要有降级路径:捕获 ExecutionRejectedException 走缓存、队列或明确错误,而不是 500 直接冒泡
  7. v7 迁移到 v8:Policy.Handle<>().WaitAndRetryAsync(...) → ResiliencePipelineBuilder().AddRetry(...);ExecuteAsync 的返回类型从 Task<T> 变成 ValueTask<T>

常见问题

问题解决
重试次数比预期多一次MaxRetryAttempts 是"额外重试次数",总调用数 = 1 + 该值
超时设置后请求仍不中断委托没有接收/使用 CancellationToken,或底层 API 不支持取消;超时策略只能协作式取消
一直抛 ExecutionRejectedException熔断已打开,等 BreakDuration 后自动半开;或检查 FailureRatio/MinimumThroughput 是否过于敏感
编译报找不到 BrokenCircuitException该类型只在 v7 的 Polly 包;用 v8 管道时改为捕获 ExecutionRejectedException
熔断一直不打开MinimumThroughput 没达到(采样窗口内调用量不足),或 ShouldHandle 没覆盖实际抛出的异常类型
升级到 v8 后编译报找不到 Policy只装了 Polly.Core;v7 旧 API 在 Polly 包里,要么换新 API,要么补装 Polly
AddResiliencePipeline 找不到缺 Polly.Extensions 包,命名管道与 DI 集成不在 Polly.Core 中
ValueTask 不能直接 await 两次v8 的 ExecuteAsync 返回 ValueTask<T>,需要 await 两次时先 .AsTask()
重试对象是 HttpResponseMessage 但没触发用了 RetryStrategyOptions 而非 RetryStrategyOptions<HttpResponseMessage>,结果判定必须带泛型
Microsoft.Extensions.Http.Resilience 版本冲突该包版本跟随 Microsoft.Extensions.*(.NET 10 用 10.x),与项目 TFM 对齐
管道里超时和重试谁耗时更长顺序决定语义:AddTimeout 在外层就是"总时长上限",在内层就是"单次尝试上限"

AI 使用建议

  • 执行环境:命令在终端 / shell 中执行(Windows 用 PowerShell);dotnet add package 需访问 NuGet。
  • 先探测版本:dotnet list package 确认 Polly.Core / Polly.Extensions / Polly.RateLimiting 同为 8.8.1;v8 的入口是 ResiliencePipelineBuilder,v7 的 Policy API 只在旧包 Polly 里,混用会编译失败。
  • 验证产物:写一个必然失败三次再成功的委托,断言 AddRetry 的实际尝试次数与退避间隔;断路器用 CircuitState 断言 Closed→Open→HalfOpen 的转换,而不是只看异常类型。
  • 常见误操:重试包住整条 HttpClient 调用链时,若外层还有 HTTP 层的重试会成倍放大请求;AddTimeout 的超时要短于 HttpClient 的 Timeout,否则永远等不到自己的超时。
  • 安全与权限:重试会放大对故障服务的压力,生产环境配指数退避与抖动;限流/熔断阈值改动前先与用户确认容量预期。

相关技能

  • serilog — 结构化日志,记录重试/熔断事件与降级路径:../serilog/SKILL.md
  • sentry — 错误追踪,熔断与最终失败可作为异常上报观测:../sentry/SKILL.md
  • furion — .NET Web 框架,结合 IHttpRemote 远程请求配置弹性:../furion/SKILL.md
  • hangfire — 后台任务重试与延迟调度(Hangfire 自带 AutomaticRetry,与 Polly 的分工要分清):../hangfire/SKILL.md
  • admin-net-backend — 企业后台常见的外部依赖(支付/短信/OSS)是弹性策略的主要落点:../admin-net-backend/SKILL.md

相关资源

Frequently asked questions

What does the Polly AI skill do?

Use when adding retry, timeout, circuit-breaker, rate-limit or fallback resilience to .NET HTTP/database calls — ResiliencePipelineBuilder AddRetry/AddTimeout/AddCircuitBreaker, Polly 8 ResiliencePipeline, Microsoft.Extensions.Http.Resilience AddStandardResilienceHandler, or fixing TimeoutRejectedException, retries multiplying load, and 'The circuit is now open'. Polly: .NET resilience and transient-fault-handling library.

Why use Polly on TypingMind?

Because you install it once and use it with any model. Polly is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Polly in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/znlgis/opengis-skills/tree/main/csharp/polly. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Polly?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Polly?

As many as you like. As long as a model supports skills, you can use Polly with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Polly AI skill free?

Yes. It is published on GitHub by znlgis under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇