使用 Polly 构建弹性 .NET 应用程序

发布: (2025年12月27日 GMT+8 12:31)
7 min read
原文: Dev.to

Source: Dev.to

现代应用很少是孤立的。它们不断与数据库、第三方 API、微服务和云资源进行通信。

因此,失败不是异常——而是预期

瞬时网络问题、临时服务中断或短暂的超时,如果不考虑弹性,极易导致本来设计良好的系统出现故障。

在本文中,我们将探讨:

  • 软件系统中 resilience 的含义
  • 常见的弹性模式
  • 如何在 .NET 中使用 Polly 实现 RetryCircuit Breaker 模式

什么是软件系统的弹性?

弹性是指应用程序能够:

  • 优雅地处理故障
  • 自动从瞬时问题中恢复
  • 保持可用性和响应性

当出现问题时,弹性系统不会崩溃或阻塞用户,而是适应并继续运行。可以把弹性机制看作 安全网——在不可避免的故障发生时捕获你的应用程序。

理解瞬态故障

瞬态故障 是一种临时性问题,通常在短时间后自行解决。常见示例包括:

  • 临时网络故障
  • API 超时
  • 短暂的数据库连接失败
  • 云服务限流

这些故障 不应视为永久性错误。在短暂延迟后重试操作通常会成功。

重试模式

什么是重试模式?

重试模式 会在放弃之前,自动重新执行失败的操作,次数有限。

何时应该使用重试?

  • 网络调用
  • 外部 API
  • 消息队列
  • 云资源

何时不 使用重试?

  • 验证错误
  • 身份验证失败
  • 业务规则违规

使用 Polly 的重试示例

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: attempt => TimeSpan.FromSeconds(2),
        onRetry: (exception, timeSpan, retryCount, context) =>
        {
            Console.WriteLine($"Retry {retryCount} after {timeSpan.TotalSeconds}s");
        });

此代码的作用

  • 将请求重试最多 3 次
  • 每次重试间隔 2 秒
  • 仅在出现 HttpRequestException 时进行重试

这种方式可以显著提升对瞬时故障的成功率,同时不影响用户体验。

电路断路器模式

电路断路器解决的问题

想象调用一个完全宕机的第三方服务:

  • 请求持续失败
  • 线程被阻塞
  • 系统资源耗尽
  • 故障在系统中蔓延

这就是小故障如何演变成系统级宕机

什么是电路断路器?

电路断路器 防止应用执行可能会失败的操作。它的工作方式类似于电气断路器:

  • 检测重复失败
  • 暂时停止发送请求
  • 让系统恢复

电路断路器状态

状态描述
Closed请求正常流通。
Open请求被立即拒绝(快速失败)。
Half‑Open允许有限数量的测试请求以检查恢复情况。

使用 Polly 的电路断路器示例

var circuitBreakerPolicy = Policy
    .Handle<HttpRequestException>()
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 5,
        durationOfBreak: TimeSpan.FromSeconds(30),
        onBreak: (exception, duration) =>
        {
            Console.WriteLine("Circuit opened");
        },
        onReset: () =>
        {
            Console.WriteLine("Circuit closed");
        },
        onHalfOpen: () =>
        {
            Console.WriteLine("Circuit half-open");
        });

此代码的作用

  • 连续 5 次失败后打开断路器
  • 阻止请求 30 秒
  • 断路期间结束后自动尝试恢复

组合重试和断路器

单独使用重试可能会使故障服务过载。
单独使用断路器可能会过于激进。

真正的威力来自于将它们结合起来。

var resiliencePolicy = Policy.WrapAsync(
    retryPolicy,
    circuitBreakerPolicy);

执行示例

await resiliencePolicy.ExecuteAsync(async () =>
{
    var response = await httpClient.GetAsync("https://external-api.com/data");
    response.EnsureSuccessStatusCode();
});

此设置确保:

  • 瞬时故障会被重试
  • 持久性故障会触发断路
  • 系统稳定性得以保持

为什么选择 Polly?

Polly 是 .NET 生态系统中事实上的弹性库,因为:

  • 流畅且富有表现力的 API
  • 支持异步和同步操作
  • 能够轻松集成 HttpClientFactory
  • 在生产系统中被广泛使用

支持的模式包括:

  • 重试
  • 熔断器
  • 超时
  • 回退
  • 隔舱隔离

实际案例

Polly 在以下场景中特别有用:

  • 微服务通信
  • 金融和交易系统
  • 云原生应用
  • API 网关
  • SignalR 与实时系统

只要存在 I/O 或远程调用的地方,弹性(resilience)就应该被视为首要关注点。

关键要点

  • 在分布式系统中,故障是不可避免的
  • 弹性(Resilience)可防止小问题演变为宕机
  • 重试(Retry)处理瞬时故障
  • 熔断器(Circuit Breaker)保护系统稳定性
  • Polly 让弹性在 .NET 中变得实用且可投入生产

最终思考

弹性不是可选功能——它是现代应用程序的设计需求。使用像 Polly 这样强大的库实现重试(Retry)和断路器(Circuit Breaker)等模式,可帮助您构建即使在进程外部环境异常时仍保持可用、响应迅速且易于维护的系统。

结论

通过利用已验证的模式和像 Polly 这样的库,您可以构建系统,使其具备:

  • 稳定
  • 可扩展
  • 容错
  • 用户友好

如果您的应用程序需要与外部世界交互,它必须具备弹性

我是 Morteza Jangjoo,致力于“解释我希望有人曾经向我解释的事”。

Back to Blog

相关文章

阅读更多 »