在大规模下优化 .NET 8 API 消耗:并发、批处理和弹性重试机制的技术深度解析

发布: (2025年12月29日 GMT+8 06:45)
5 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将按照要求将其翻译为简体中文并保留原始的格式、Markdown 语法以及技术术语。

介绍

在构建依赖外部 API 的系统时,预见并缓解潜在的扩展瓶颈(如速率限制)至关重要。本文详细介绍了使用 .NET 8 的技术策略,成功将对第三方 API 的调用量从最初的 500+ 请求扩展到超过 10,000,显著降低了处理时间和错误率。

初始实现

  • 处理时间: ~30 分钟,针对10,000个请求
  • 失败率: ~50 %(主要是 429 Too Many Requests 错误)

工作负载包括遍历10,000个唯一 URL,每个 URL 都需要单独的 API 调用,例如:

https://www.example.com/get?id=123

核心优化

批处理

与其顺序处理所有请求或用并发调用淹没 API,我们引入了结构化的批处理策略。请求被分组为批次,每个批次仅在前一个批次完成后才发送。

动态延迟计算

为了在不违反速率限制的前提下最大化吞吐量,批次之间的延迟会动态计算:

// Delay for the next batch (in seconds)
double delayNext = 10.0 - processingTimePrevious;

如果 processingTimePrevious 超过 10 秒,延迟将变为零,允许立即发送下一个批次。

受控并发与弹性

使用 .NET 8 SemaphoreSlim 原语强制执行全局并发限制:

private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(50); // max 50 concurrent requests

public async Task SendAsync(HttpRequestMessage request)
{
    await _semaphore.WaitAsync();
    try
    {
        return await _httpClient.SendAsync(request);
    }
    finally
    {
        _semaphore.Release();
    }
}
  • 将并发的外部调用限制为 50
  • 防止对外部 API 造成过载,并控制内存/线程使用。

异步处理与队列

管道在整个过程中利用 async/await,使线程池在 I/O‑bound API 调用进行时保持响应。请求在内部排队,工作线程在遵守信号量限制的前提下出队项目。

弹性策略 (Microsoft.Extensions.Resilience)

重试策略

var retryPolicy = Policy
    .Handle()
    .OrResult(r => r.StatusCode == HttpStatusCode.TooManyRequests)
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))
    );
  • 对瞬态故障进行指数退避重试。
  • 专门针对 429 响应。

熔断器策略

var circuitBreaker = Policy
    .Handle()
    .OrResult(r => r.StatusCode == HttpStatusCode.TooManyRequests)
    .CircuitBreakerAsync(
        handledEventsAllowedBeforeBreaking: 20,
        durationOfBreak: TimeSpan.FromSeconds(30)
    );
  • 在连续 20 次失败后打开熔断,暂停请求 30 秒后再允许流量通过。

性能提升

将批处理、动态延迟和弹性重试逻辑相结合,带来了显著的改进:

指标优化前优化后
总处理时间~30 分钟~15 分钟(≈ 50 % 减少)
失败率(429~50 %< 5 %
平均并发调用数~5–1050(受控)

结论与未来可扩展性

通过在 .NET 8 中集成并发控制、批处理和稳健的重试机制,将一个缓慢的 API 处理管道转变为更高效的系统,处理时间大约缩短了一半。此经验强调了以下重要性:

  • 理解第三方 API 的限制。
  • 利用批处理实现可扩展性。
  • 应用诸如重试和断路器等弹性模式。

未来可扩展性工作

  • 基于实时延迟指标的自适应批量大小。
  • 分布式处理(例如使用 Azure Functions 或 Kubernetes)以处理更大规模的请求。
  • 监控并自动调优速率限制阈值。

这些优化为处理大规模 API 请求提供了坚实的基础,并且在请求量持续增长的情况下仍有充足的改进空间。

Back to Blog

相关文章

阅读更多 »