C#:使用 Polly 对传出 HTTP 请求进行节流/速率限制

Ail*_*n79 2 c# rate-limiting dotnet-httpclient resiliency polly

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

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

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),
        }));
Run Code Online (Sandbox Code Playgroud)

这只是在失败时重试的弹性。我在单例 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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"));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

然后我尝试了一下 Polly 的 Bulkhead 策略,但据我所知,这是为了限制并行执行的数量。

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

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

Ail*_*n79 5

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

我安装了David Desmaisons RateLimiter 软件包,它使用起来非常简单。在我的单身人士中,我现在有这个:

public TimeLimiter RateLimiter = TimeLimiter.GetFromMaxCountByInterval(150, TimeSpan.FromSeconds(10));
Run Code Online (Sandbox Code Playgroud)

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

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

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


Pet*_*ala 5

在这篇文章中,我想澄清有关速率限制器速率门的事情

相似

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

不同之处

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

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

算法