我使用HttpClientFactory和Refit一起进行API调用。我想使用Polly进行重试,断路器和回退,虽然设置重试和断路器很容易,但我与回退斗争,因为它只能返回类型HttpResponseMessage
的回退值,但返回的是Refit.ApiResponse
。我试着返回一个假的HttpResponseMessage
,但随后Refit中断,因为它期望某些事情存在,例如HttpRequestMessage
。
当使用HttpClientFactory
与Refit时,是否有人成功返回自定义对象作为回退值?
这是重试的初始设置:
PolicyBuilder<HttpResponseMessage>? policyBuilder = Policy<HttpResponseMessage>
.Handle<Exception>();
IEnumerable<TimeSpan>? retryDelays = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5, fastFirst:true);
AsyncRetryPolicy<HttpResponseMessage>? retryPolicy = policyBuilder.WaitAndRetryAsync(retryDelays, (exception, timeSpan, retryCount, context) =>
{
Debug.WriteLine($"Exception occurred while called Account Service. Retry policy in effect | Retry Attempt: {retryCount} | WaitSeconds: {timeSpan.TotalSeconds}. Exception: {exception.Exception.Message}");
});
这是断路器:
AsyncCircuitBreakerPolicy<HttpResponseMessage>? circuitBreakerPolicy = policyBuilder
.CircuitBreakerAsync(breakCircuitAfterErrors, TimeSpan.FromMinutes(keepCircuitBreakForMinutes),
(exception, timespan, context) =>
{
// OnBreak, i.e. when circuit state changes to open
Debug.WriteLine(
$"Account Service circuit breaker policy in effect | State changed to Open (blocked). It will remain open for {keepCircuitBreakForMinutes} minutes");
},
(context) =>
{
// OnReset, i.e. when circuit state changes to closed
Debug.WriteLine("Account Service circuit breaker policy in effect | State changed to Closed (normal).");
});
这是HttpResponseMessage
的回退,它打破了Refit:
AsyncFallbackPolicy<HttpResponseMessage> fallbackPolicyForCircuitBreaker = Policy<HttpResponseMessage>
.Handle<BrokenCircuitException>()
.FallbackAsync((cancellationToken) =>
{
// In our case we return a null response.
Debug.WriteLine($"The Circuit is Open (blocked). A fallback null value is returned. Try again later.");
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
});
这里我将策略处理程序添加到客户端:
builder.Services.AddHttpClient("AccountService")
.AddPolicyHandler(fallbackPolicyForCircuitBreaker)
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy);
FallbackAsync
有几个过载。其中一个是这样的:
public static AsyncFallbackPolicy<TResult> FallbackAsync<TResult>(
this PolicyBuilder<TResult> policyBuilder,
Func<DelegateResult<TResult>, Context, CancellationToken, Task<TResult>> fallbackAction,
Func<DelegateResult<TResult>, Context, Task> onFallbackAsync)
这意味着在fallbackAction
的情况下,您可以通过DelegateResult
访问故障的HttpResponseMessage
。
.FallbackAsync(
fallbackAction:(dr, ctx, ct) => Task.FromResult(new HttpResponseMessage(dr.Result.StatusCode)),
onFallbackAsync:(dr, ctx) => Task.CompletedTask))
但请记住,dr.Result
只有在没有例外的情况下才存在。否则.Result
将是null
,.Exception
将包含抛出的异常。
如果你需要访问请求对象,那么你可以通过闭包来做。AddPolicyHandler
有以下重载:
public static IHttpClientBuilder AddPolicyHandler (
this IHttpClientBuilder builder,
Func<HttpRequestMessage,IAsyncPolicy<HttpResponseMessage>> policySelector);
public static IHttpClientBuilder AddPolicyHandler (
this IHttpClientBuilder builder,
Func<IServiceProvider,HttpRequestMessage,IAsyncPolicy<HttpResponseMessage>> policySelector);
因此,在注册期间,您可以执行以下操作:
.AddPolicyHandler(req => Policy<HttpResponseMessage>
.Handle<BrokenCircuitException>()
.FallbackAsync((...) =>
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { RequestMessage = req });
}, ...));