Polly: Honoring Retry-After, после чего сообщается с пользовательским исключением? - PullRequest
0 голосов
/ 05 марта 2020

Так что мне нужно использовать библиотеку, которая, по сути, выполняет POST для удаленной системы, которая может выбрать ограничение трафика c. Если это так, он возвращает 429 и заданные c # секунд для отката в заголовке Retry-After ... в этот момент платформа считывает и анализирует это значение, и, по сути, делает это

throw new ThrottledException(retryAfterSeconds);

Как настроить политику Polly, которая будет перехватывать это пользовательское исключение, а затем повторять попытку через exception.RetryAfter секунд?

1 Ответ

0 голосов
/ 10 марта 2020

ОК, это было немного сложнее, чем нужно, но только потому, что я был отправлен на несколько диких goose погонь от непостижимых сообщений компилятора.

В этом сценарии повтор повторяется через пользовательское исключение типа SigsThrottledException, в котором есть поле, содержащее запрошенное время отката в секундах.

var policy = Policy
    .Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
    .WaitAndRetryAsync(
        retryCount: retries,
        sleepDurationProvider: (i, e, ctx) =>
        {
            var ste = (SigsThrottledException)e;
            return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
        },
        onRetryAsync: async (e, ts, i, ctx) =>
        {
            // Do something here
        };);

Это пример использования политики. Вы не можете просто добавить его в существующий HttpClient или HttpClientFactory . Вы должны использовать это явно.

[TestMethod]
public async Task SigsPollyRetriesOnThrottle()
{
    var retryResponse = new HttpResponseMessage
    {
        StatusCode = (HttpStatusCode)429,
        Content = new StringContent("{}"),
    };

    retryResponse.Headers.Add("Retry-After", "1");

    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(retryResponse)
        .ReturnsAsync(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK
        });

    var client = new HttpClient(mockMessageHandler.Object);

    // Retry once after waiting 1 second
    var retryPolicy = Policy
    .Handle<SigsThrottledException>(e => e.RetryAfterInSeconds > 0)
    .WaitAndRetryAsync(
        retryCount: 1,
        sleepDurationProvider: (i, e, ctx) =>
        {
            var ste = (SigsThrottledException)e;
            return TimeSpan.FromSeconds((double)ste.RetryAfterInSeconds);
        },
        onRetryAsync: async (e, ts, i, ctx) =>
        {
            // Do something here
        };);

    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();
    var response = await retryPolicy.ExecuteAsync(async () =>
    {
        Uri substrateurl = new Uri("https://substrate.office.com/");
        return await SIGSClient.Instance.PostAsync(client, substrateurl, new UserInfo(), "faketoken", new Signal(), Guid.NewGuid()).ConfigureAwait(false);
    }
    );

    Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
    stopWatch.Stop();
    Assert.IsTrue(stopWatch.ElapsedMilliseconds > 1000); // Make sure we actually waited at least a second
}
...