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 实现这一点有什么建议吗?(或者任何其他扩展,如果有充分的理由的话)
再次感谢@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 的期望。