简体   繁体   English

C#:使用 Polly 发出 HTTP 请求的节流/速率限制

[英]C#: Throttle/rate limit outgoing HTTP requests with Polly

I am developing an integration solution that accesses a rate limited API.我正在开发一个访问速率受限的 API 的集成解决方案。 I am performing a variety of CRUD operations on the API using multiple HTTP verbs on different endpoints (on the same server though).我正在 API 上执行各种 CRUD 操作,在不同的端点上使用多个 HTTP 动词(尽管在同一台服务器上)。 I have been pointed towards Polly multiple times, but I haven't managed to come up with a solution that actually works.我曾多次提到波莉,但我还没有想出一个真正有效的解决方案。

This is what I have in my startup:这就是我在启动时所拥有的:

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),
        }));

This is just resilience to retry in case of failure.这只是在失败时重试的弹性。 I have a RateLimit policy in a singleton ApiWrapper class:我在 singleton ApiWrapper class 中有一个 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");
    }
}

That policy is used in multiple other classes like this:该策略用于多个其他类,如下所示:

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"));
        }
    }
}

My expectation was that the rate limiter would not fire more requests than configured, but that does not seem to be true.我的期望是速率限制器不会触发比配置更多的请求,但这似乎不是真的。 From my understanding the way it works is that the rate limiter just throws an exception if there are more calls than the limit that has been configured.据我了解,它的工作方式是,如果调用次数超过已配置的限制,速率限制器只会引发异常。 That's where I thought the Retry policy would come into play, so just try again after 5 or 15 seconds if it did not go through the limiter.这就是我认为重试策略会发挥作用的地方,所以如果它没有通过限制器 go,请在 5 或 15 秒后重试。

Then I played around a bit with Polly's Bulkhead policy, but as far as I can see that is meant to limit the amount of parallel executions.然后我玩了一下 Polly 的 Bulkhead 策略,但据我所知,这是为了限制并行执行的数量。

I have multiple threads that may use different HttpClients (all created by the Factory like in the example above) with different methods and endpoints, but all use the same policies.我有多个线程可能使用不同的 HttpClients(全部由工厂创建,如上例所示)具有不同的方法和端点,但都使用相同的策略。 Some threads run in parallel, some sequentially as I have to wait for their response before sending the next requests.有些线程并行运行,有些是顺序运行的,因为我必须在发送下一个请求之前等待它们的响应。

Any suggestions on how this can or should be achieved with Polly?关于如何通过 Polly 实现或应该实现这一点的任何建议? (Or any other extension if there is good reason to) (或任何其他扩展,如果有充分的理由)

In this post I would like to clarify things around rate limiter and rate gate在这篇文章中,我想澄清有关速率限制器速率门的事情

Similarity相似

  • Both concepts can be used to throttle requests.这两个概念都可以用来限制请求。
  • They sit between the clients and the server and they know about the server's capacity.它们位于客户端和服务器之间,并且了解服务器的容量。

Difference区别

  • The limiter as its name implies limits the transient traffic.顾名思义,限制器限制了瞬态流量。 It short-cuts the requests if there are too many.如果请求太多,它会缩短请求。

  • The gate on the other hand holds/delays the requests until there is enough capacity.另一方面,门保持/延迟请求,直到有足够的容量。

Algorithms算法

Thanks again to @Neil and @Panagiotis for pointing me in the right direction.再次感谢@Neil 和@Panagiotis 为我指明了正确的方向。 I wrongly assumed that the Polly rate limiter would actually delay API calls.我错误地认为 Polly 速率限制器实际上会延迟 API 调用。 I found a workaround that probably is not particularly nice, but for my purpose it does the trick.我发现了一种可能不是特别好的解决方法,但就我的目的而言,它可以解决问题。

I installed David Desmaisons RateLimiter package which is super simple to use.我安装了David Desmaisons RateLimiter package ,它使用起来超级简单。 In my singleton I have now this:在我的 singleton 我现在有这个:

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

I use this RateLimiter everywhere I make calls to an API endpoint like this:我在调用 API 端点的任何地方都使用这个 RateLimiter,如下所示:

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

Does exactly what I originally expected from Polly.完全符合我最初对 Polly 的期望。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM