【问题标题】:C#: Throttle/rate limit outgoing HTTP requests with PollyC#:使用 Polly 限制传出 HTTP 请求
【发布时间】:2022-10-02 04:57:53
【问题描述】:

我正在开发一个访问速率受限 API 的集成解决方案。我正在使用不同端点上的多个 HTTP 动词对 API 执行各种 CRUD 操作(尽管在同一台服务器上)。我曾多次提到波莉,但我还没有想出一个真正有效的解决方案。

这就是我在我的创业公司中所拥有的:

builder.Services
    .AddHttpClient(\"APIClient\", client =>
    {
        client.BaseAddress = new Uri(C.Configuration.GetValue<string>(\"APIBaseAddress\"));
    })
    .AddTransientHttpErrorPolicy(builder => 
        builder.WaitAndRetryAsync(new []
        {
           TimeSpan.FromSeconds(1),
           TimeSpan.FromSeconds(5),
           TimeSpan.FromSeconds(15),
        }));

这只是在失败时重试的弹性。我在单例 ApiWrapper 类中有一个 RateLimit 策略:

public sealed class ApiWrapper
{
    private static readonly Lazy<ApiWrapper> lazy = new Lazy<ApiWrapper>(() => new ApiWrapper());
    public static ApiWrapper Instance { get { return lazy.Value; } }
    private IHttpClientFactory _httpClientFactory;
    public readonly AsyncRateLimitPolicy RateLimit = Policy.RateLimitAsync(150, TimeSpan.FromSeconds(10), 50); // 150 actions within 10 sec, 50 burst

    private ApiWrapper()
    {
    }

    public void SetFactory(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public HttpClient GetApiClient()
    {
        return _httpClientFactory.CreateClient(\"APIClient\");
    }
}

该策略在多个其他类中使用,如下所示:

public class ApiConsumer
{
    private HttpClient _httpClient = ApiWrapper.Instance.GetApiClient();

    public async Task<bool> DoSomethingWithA(List<int> customerIDs)
    {
        foreach (int id in customerIDs)
        {
            HttpResponseMessage httpResponse = await ApiWrapper.Instance.RateLimit.ExecuteAsync(() => _httpClient.GetAsync($\"http://some.endpoint\"));
        }
    }
}

我的期望是速率限制器不会触发比配置更多的请求,但这似乎不是真的。据我了解,它的工作方式是,如果调用次数超过已配置的限制,速率限制器只会引发异常。这就是我认为重试策略会发挥作用的地方,所以如果它没有通过限制器,请在 5 或 15 秒后重试。

然后我玩了一下 Polly 的 Bulkhead 策略,但据我所知,这意味着限制并行执行的数量。

我有多个线程可能使用不同的 HttpClients(全部由工厂创建,如上例所示)具有不同的方法和端点,但都使用相同的策略。有些线程并行运行,有些是顺序运行的,因为我必须在发送下一个请求之前等待它们的响应。

关于如何通过 Polly 实现或应该实现这一点的任何建议? (或任何其他扩展,如果有充分的理由)

  • 对于某些 API,当它们返回 429 Too Many Requests 时,响应包含一个参数,说明何时重试(以秒为单位或绝对时间)。所以基本上,API 会告诉你什么时候再试一次,而不是你再试一次然后立即被拒绝。
  • 那不是波莉的工作。 Polly 用于恢复、重试和确保您不超过速率限制。但是,您将如何处理受限制的请求?你会拒绝他们吗?排队?这是一个重要的应用程序特性,而不是你可以从库中获得的东西
  • 发出这些请求的代码呢?您是否允许它继续生成无法服务的请求,或者您是否使用背压机制告诉它减速?那是 Channels、Dataflow 库、Parallel.ForEachAsync 等的工作。
  • 谢谢大家的提醒。假设 Polly 只会将这些请求排队并仅将它们一一发送以确保不达到速率限制,可能太简单了。 @Panagiotis:我假设我需要创建一个队列,一些机制来处理队列中的项目并将响应返回给请求线程。有没有你推荐看的扩展/框架?将看一下 Dataflow,但不确定它是否是我需要的,因为请求本身可以按顺序触发,不需要并行...(对不起,我对 C# 很陌生)
  • @Neil:我一开始不想得到 429 ......

标签: c# dotnet-httpclient rate-limiting polly resiliency


【解决方案1】:

再次感谢@Neil 和@Panagiotis 为我指明了正确的方向。我错误地认为 Polly 速率限制器实际上会延迟 API 调用。我找到了一个可能不是特别好的解决方法,但就我的目的而言,它可以解决问题。

我安装了David Desmaisons RateLimiter package,它使用起来超级简单。在我的单身中,我现在有这个:

public TimeLimiter RateLimiter = TimeLimiter.GetFromMaxCountByInterval(150, TimeSpan.FromSeconds(10));

我在调用 API 端点的任何地方都使用这个 RateLimiter,如下所示:

HttpResponseMessage httpResponse = await ApiWrapper.Instance.RateLimiter.Enqueue(() => _httpClient.GetAsync($"http://some.endpoint"), _cancellationToken);

完全符合我最初对 Polly 的期望。

【讨论】:

    【解决方案2】:

    在这篇文章中,我想澄清一下周围的事情限速器费率门

    相似

    • 这两个概念都可以用来限制请求。
    • 它们位于客户端和服务器之间,并且了解服务器的容量。

    区别

    • 顾名思义,限制器限制了瞬态流量。如果请求太多,它会缩短请求。

    • 另一方面,门保持/延迟请求,直到有足够的容量。

    算法

    【讨论】:

    • 非常感谢您澄清术语,我不知道这一点。显然,我现在使用的 RateLimiter 包的作者也没有意识到这一点,因此该包实际上应该命名为 RateGate 或类似的名称,因为它清楚地实现了计时功能。
    • @Aileron79 是的,该包的正确名称应该使用速率门。
    猜你喜欢
    • 1970-01-01
    • 2020-08-09
    • 2016-06-11
    • 2023-03-27
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    相关资源
    最近更新 更多