public async Task PostAsync(string resource, TRequest payload) where TRequest : class
{
var request = new RestRequest(resource);
request.AddJsonBody(payload);
var response = await PolicyFactory.RestSharpPolicy(_logger).ExecuteAsync(
async token => await _client.ExecuteAsync(request, Method.Post, token));
LogAndRaiseErrors(request, response);
return response.Data.Data;
}
Вот метод LogAndRaiseErrors:
protected void LogAndRaiseErrors(RestRequest request, RestResponse response)
{
if (response.StatusCode != HttpStatusCode.OK)
{
_logger.LogError("API Error:{response}", SerializeResponse(response));
throw new BigCommerceException(response.Content);
}
_logger.LogDebug("B2B API Call:\n{response}", SerializeResponse(response));
}
Я просмотрел документацию Polly, но она немного скудна.
Как мне построить ResiliencePipeline, используемый в PostAsync метод для достижения следующих основных целей:
- Прочитайте ответ.Headers.FirstOrDefault(h=>h.Name?.ToLower() == "retry -after")?.Значение и задержка для указанных секунд, чего существующий код вообще не делает.
- Я считаю, что повторная попытка после должна влиять на все потоки, которые могут вызывать данный API? добавить автоматический выключатель?
- Регистрируйте каждый запрос (в настоящее время это делается в LogAndRaiseErrorsMethod).
- Регистрируйте повторные попытки, включая задержку, указанную в заголовке.
- Переместите LogAndRaiseErrors(... ) в политику, чтобы политика могла обрабатывать журналирование и любые исключения.
- Добавьте политику для повторных попыток в случае сетевых сбоев/тайм-аутов и т. д.
- Я подозреваю, что нам нужен некоторый джиттер (это для чего он нужен?), чтобы не все потоки одновременно загружали API?
Я думаю, что повторная попытка работала правильно, но не автоматический выключатель.
Я перенес создание конвейера в конструктор, поэтому существует только один конвейер (журнал показывает, что это звонили один раз)
public ApiClient(..., ILogger logger)
{
...
_client = client;
_resiliencePipeline = PolicyFactory.GetRestSharpPolicy(_logger);
}
public async Task GetAsync(string resource)
{...}
public async Task PostAsync(string resource, TRequest payload) where TRequest : class
{
var request = new RestRequest(resource);
request.AddJsonBody(payload);
var response = await _resiliencePipeline.ExecuteAsync(
async token => await _client.ExecuteAsync(request, Method.Post, token));
LogAndRaiseErrors(request, response);
return response.Data.Data;
}
Вот мой конвейер, но я никогда не вижу записей в журнале прерывателя цепи, но вижу много записей в журнале повторных попыток.
public static class PolicyFactory
{
public static ResiliencePipeline GetRestSharpPolicy(ILogger logger)
{
logger.LogInformation("Building ResiliencePipeline");
return new ResiliencePipelineBuilder()
.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
FailureRatio = 0,
ShouldHandle = new PredicateBuilder()
.HandleResult(static result => result.StatusCode == HttpStatusCode.TooManyRequests),
OnOpened = args =>
{
logger.LogWarning("Circuit Breaker Opened on {StatusCode} for {Duration}s ({ResponseUri})",
args.Outcome.Result.StatusCode, args.BreakDuration.TotalSeconds, args.Outcome.Result.ResponseUri);
return ValueTask.CompletedTask;
},
OnClosed = args =>
{
logger.LogWarning("Circuit Breaker Closed on {StatusCode} ({ResponseUri})",
args.Outcome.Result.StatusCode, args.Outcome.Result.ResponseUri);
return ValueTask.CompletedTask;
}
})
.AddRetry(new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder()
.HandleResult(static result => result.StatusCode == HttpStatusCode.TooManyRequests),
DelayGenerator = delayArgs =>
{
var retryAfter = delayArgs.Outcome.Result.Headers.FirstOrDefault(h => h.Name?.ToLower() == "retry-after")?.Value.ToString();
return int.TryParse(retryAfter, out var seconds)
? new ValueTask(TimeSpan.FromSeconds(seconds))
: new ValueTask(TimeSpan.FromSeconds(0.5));
},
MaxRetryAttempts = 5,
OnRetry = args =>
{
logger.LogWarning("Retry Attempt:{Attempt} Delay:{Delay}",
args.AttemptNumber, args.RetryDelay);
return ValueTask.CompletedTask;
}
})
.Build();
}
}
Подробнее здесь: https://stackoverflow.com/questions/787 ... ipeline-to