在使用 Polly 重试之前检查响应的字符串内容
Check string content of response before retrying with Polly
我正在使用一个非常不稳定的 API。有时我得到 500 Server Error
和 Timeout
,有时我也得到 500 Server Error
因为我给它输入了它无法处理的 SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
.
这两种情况都给我 HttpRequestException
但我可以查看来自服务器的回复消息并确定异常的原因。如果是超时错误,我应该再试一次。如果是错误的输入,我应该重新抛出异常,因为再多的重试都无法解决错误数据的问题。
我想对 Polly 做的是在尝试重试之前检查响应消息。但是到目前为止我看到的所有样本都只包含异常类型。
到目前为止我已经想到了这个:
HttpResponseMessage response = null;
String stringContent = null;
Policy.Handle<FlakyApiException>()
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
async (exception, timeSpan, context) =>
{
response = await client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"));
stringContent = await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("Timeout"))
{
throw new FlakyApiException(stringContent);
}
});
有没有更好的方法来进行这种检查?
通常,您可以配置 Polly 策略以响应执行结果(而不仅仅是异常),例如使用谓词检查 HttpResponseMessage.StatusCode
。示例 here in the Polly readme.
但是没有内置方法来配置单个 Polly 策略来额外响应响应消息的内容。这是因为(如您的示例所示)获取该内容需要第二次异步调用,这本身可能会引发网络错误。
这 tl;dr 导致如何表达(以简单语法)单一策略的复杂性,该策略管理两个不同的异步步骤,每个步骤可能有不同的错误处理。之前的相关讨论on Polly github:欢迎评论。
因此,如果序列需要两个单独的异步调用,Polly 团队目前建议将其表示为两个单独的策略,类似于 the example in the end of this answer。
您问题中的特定示例可能不起作用,因为 onRetryAsync
委托(抛出 FlakyApiException
)本身不受策略保护。一个策略只保护通过 .Execute/ExecuteAsync(...)
.
执行的委托的执行
一种方法是使用两种策略,一种重试策略,重试所有典型的 http 异常和状态代码,包括 500s;然后在其中一个 Polly FallbackPolicy,它捕获表示 SqlDateTime overflow
的状态代码 500,并通过重新抛出某些区别异常(CustomSqlDateOverflowException
)来排除它的重试。
IAsyncPolicy<HttpResponseMessage> rejectSqlError = Policy<HttpResponseMessage>
.HandleResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
.FallbackAsync(async (delegateOutcome, context, token) =>
{
String stringContent = await delegateOutcome.Result.Content.ReadAsStringAsync(); // Could wrap this line in an additional policy as desired.
if (delegateOutcome.Result.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("SqlDateTime overflow"))
{
throw new CustomSqlDateOverflowException(); // Replace 500 SqlDateTime overflow with something else.
}
else
{
return delegateOutcome.Result; // render all other 500s as they were
}
}, async (delegateOutcome, context) => { /* log (if desired) that InternalServerError was checked for what kind */ });
IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
.OrResult(r => /* condition for any other errors you want to handle */)
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
async (exception, timeSpan, context) =>
{
/* log (if desired) retry being invoked */
});
HttpResponseMessage response = await retryPolicy.WrapAsync(rejectSqlError)
.ExecuteAsync(() => client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"), cancellationToken));
如果我对你的问题的理解正确,那么你只想在状态代码为 500 且正文包含 Timeout
时重试。如果是这种情况,那么您可以像这样定义您的政策
Policy<HttpResponseMessage>
.HandleResult(response =>
response.StatusCode == System.Net.HttpStatusCode.InternalServerError
&& response.Content.ReadAsStringAsync().GetAwaiter().GetResult().Contains("Timeout"))
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt);
我正在使用一个非常不稳定的 API。有时我得到 500 Server Error
和 Timeout
,有时我也得到 500 Server Error
因为我给它输入了它无法处理的 SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
.
这两种情况都给我 HttpRequestException
但我可以查看来自服务器的回复消息并确定异常的原因。如果是超时错误,我应该再试一次。如果是错误的输入,我应该重新抛出异常,因为再多的重试都无法解决错误数据的问题。
我想对 Polly 做的是在尝试重试之前检查响应消息。但是到目前为止我看到的所有样本都只包含异常类型。
到目前为止我已经想到了这个:
HttpResponseMessage response = null;
String stringContent = null;
Policy.Handle<FlakyApiException>()
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
async (exception, timeSpan, context) =>
{
response = await client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"));
stringContent = await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("Timeout"))
{
throw new FlakyApiException(stringContent);
}
});
有没有更好的方法来进行这种检查?
通常,您可以配置 Polly 策略以响应执行结果(而不仅仅是异常),例如使用谓词检查 HttpResponseMessage.StatusCode
。示例 here in the Polly readme.
但是没有内置方法来配置单个 Polly 策略来额外响应响应消息的内容。这是因为(如您的示例所示)获取该内容需要第二次异步调用,这本身可能会引发网络错误。
这 tl;dr 导致如何表达(以简单语法)单一策略的复杂性,该策略管理两个不同的异步步骤,每个步骤可能有不同的错误处理。之前的相关讨论on Polly github:欢迎评论。
因此,如果序列需要两个单独的异步调用,Polly 团队目前建议将其表示为两个单独的策略,类似于 the example in the end of this answer。
您问题中的特定示例可能不起作用,因为 onRetryAsync
委托(抛出 FlakyApiException
)本身不受策略保护。一个策略只保护通过 .Execute/ExecuteAsync(...)
.
一种方法是使用两种策略,一种重试策略,重试所有典型的 http 异常和状态代码,包括 500s;然后在其中一个 Polly FallbackPolicy,它捕获表示 SqlDateTime overflow
的状态代码 500,并通过重新抛出某些区别异常(CustomSqlDateOverflowException
)来排除它的重试。
IAsyncPolicy<HttpResponseMessage> rejectSqlError = Policy<HttpResponseMessage>
.HandleResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
.FallbackAsync(async (delegateOutcome, context, token) =>
{
String stringContent = await delegateOutcome.Result.Content.ReadAsStringAsync(); // Could wrap this line in an additional policy as desired.
if (delegateOutcome.Result.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("SqlDateTime overflow"))
{
throw new CustomSqlDateOverflowException(); // Replace 500 SqlDateTime overflow with something else.
}
else
{
return delegateOutcome.Result; // render all other 500s as they were
}
}, async (delegateOutcome, context) => { /* log (if desired) that InternalServerError was checked for what kind */ });
IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
.OrResult(r => /* condition for any other errors you want to handle */)
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
async (exception, timeSpan, context) =>
{
/* log (if desired) retry being invoked */
});
HttpResponseMessage response = await retryPolicy.WrapAsync(rejectSqlError)
.ExecuteAsync(() => client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"), cancellationToken));
如果我对你的问题的理解正确,那么你只想在状态代码为 500 且正文包含 Timeout
时重试。如果是这种情况,那么您可以像这样定义您的政策
Policy<HttpResponseMessage>
.HandleResult(response =>
response.StatusCode == System.Net.HttpStatusCode.InternalServerError
&& response.Content.ReadAsStringAsync().GetAwaiter().GetResult().Contains("Timeout"))
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt);